Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
e72aa1c853 | |||
3c02969ffd | |||
7a3fd4e400 | |||
d10f31109b | |||
eb5d136207 | |||
32ddcdc269 | |||
bb2a195007 | |||
3c797f0850 | |||
715eedf4bf | |||
fa74f7ad4c | |||
d7738f79e1 | |||
1a1a8e0f98 | |||
5b0f744a75 | |||
f79bc57d83 | |||
9b2e68ae0a | |||
9e2662d1dd | |||
2daa8927d1 | |||
6c77212eba | |||
eb7ea55016 | |||
5ef001a877 | |||
08fd6fab59 | |||
3a87c3e8ef | |||
9f8c4f4135 | |||
14decf2db8 | |||
5bfdaf5974 |
@ -1,4 +1,4 @@
|
|||||||
FROM debian:bullseye-slim
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
ADD dip /dip
|
ADD dip /dip
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# dip
|
# dip
|
||||||
[![Build Status](https://drone.paulbsd.com/api/badges/paulbsd/dip/status.svg)](https://drone.paulbsd.com/paulbsd/dip)
|
[![Build Status](https://drone.paulbsd.com/api/badges/paulbsd/dip/status.svg)](https://drone.paulbsd.com/paulbsd/dip)
|
||||||
|
|
||||||
dip is a small webservice designed to return public ip address
|
dip is a small webservice retrieving the IP addressing information
|
||||||
|
|
||||||
## Howto
|
## Howto
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ curl -H "Accept: text/html" http://localhost:8080/
|
|||||||
## License
|
## License
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Copyright (c) 2019, 2020, 2021, 2022 PaulBSD
|
Copyright (c) 2020, 2021, 2022, 2023 PaulBSD
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
"git.paulbsd.com/paulbsd/dip/src/config"
|
"git.paulbsd.com/paulbsd/dip/src/config"
|
||||||
"git.paulbsd.com/paulbsd/dip/src/dip"
|
"git.paulbsd.com/paulbsd/dip/src/dip"
|
||||||
"git.paulbsd.com/paulbsd/dip/src/ws"
|
"git.paulbsd.com/paulbsd/dip/src/ws"
|
||||||
@ -18,5 +20,11 @@ func main() {
|
|||||||
ws := ws.WS{}
|
ws := ws.WS{}
|
||||||
ws.Page.Title = "Public IP Address Service"
|
ws.Page.Title = "Public IP Address Service"
|
||||||
dip.Init()
|
dip.Init()
|
||||||
|
|
||||||
|
memcacheconn, ok := os.LookupEnv("MEMCACHECONN")
|
||||||
|
if ok {
|
||||||
|
dip.MemcacheConn = memcacheconn
|
||||||
|
}
|
||||||
|
|
||||||
ws.RunServer(config, Templates, Static)
|
ws.RunServer(config, Templates, Static)
|
||||||
}
|
}
|
||||||
|
34
go.mod
34
go.mod
@ -1,29 +1,27 @@
|
|||||||
module git.paulbsd.com/paulbsd/dip
|
module git.paulbsd.com/paulbsd/dip
|
||||||
|
|
||||||
go 1.19
|
go 1.23
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/labstack/echo/v4 v4.10.0
|
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/labstack/echo/v4 v4.12.0
|
||||||
golang.org/x/crypto v0.5.0 // indirect
|
github.com/likexian/whois v1.15.4
|
||||||
golang.org/x/sys v0.4.0 // indirect
|
github.com/likexian/whois-parser v1.24.19
|
||||||
golang.org/x/text v0.6.0 // indirect
|
github.com/oschwald/geoip2-golang v1.11.0
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/likexian/whois v1.14.4
|
|
||||||
github.com/likexian/whois-parser v1.24.2
|
|
||||||
github.com/oschwald/geoip2-golang v1.8.0
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||||
github.com/labstack/gommon v0.4.0 // indirect
|
github.com/labstack/gommon v0.4.2 // indirect
|
||||||
github.com/likexian/gokit v0.25.9 // indirect
|
github.com/likexian/gokit v0.25.15 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/oschwald/maxminddb-golang v1.10.0 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/oschwald/maxminddb-golang v1.13.1 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||||
golang.org/x/net v0.5.0 // indirect
|
golang.org/x/crypto v0.26.0 // indirect
|
||||||
golang.org/x/time v0.3.0 // indirect
|
golang.org/x/net v0.28.0 // indirect
|
||||||
|
golang.org/x/sys v0.24.0 // indirect
|
||||||
|
golang.org/x/text v0.17.0 // indirect
|
||||||
|
golang.org/x/time v0.6.0 // indirect
|
||||||
)
|
)
|
||||||
|
85
go.sum
85
go.sum
@ -1,54 +1,61 @@
|
|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous=
|
||||||
|
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
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/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/echo/v4 v4.10.0 h1:5CiyngihEO4HXsz3vVsJn7f8xAlWwRr3aY6Ih280ZKA=
|
github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0=
|
||||||
github.com/labstack/echo/v4 v4.10.0/go.mod h1:S/T/5fy/GigaXnHTkh0ZGe4LpkkQysvRjFMSUTkDRNQ=
|
github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM=
|
||||||
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
|
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||||
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
|
||||||
github.com/likexian/gokit v0.25.9 h1:rzSQ/dP7Qw+QUzSuWlrLF0AtZS3Di6uO5yWOKhx2Gk4=
|
github.com/likexian/gokit v0.25.15 h1:QjospM1eXhdMMHwZRpMKKAHY/Wig9wgcREmLtf9NslY=
|
||||||
github.com/likexian/gokit v0.25.9/go.mod h1:oDDqJUcnnF9uAKuw54F7s6oEG+OJ7eallfDW2dq0A/o=
|
github.com/likexian/gokit v0.25.15/go.mod h1:S2QisdsxLEHWeD/XI0QMVeggp+jbxYqUxMvSBil7MRg=
|
||||||
github.com/likexian/whois v1.14.4 h1:yNQVugAHfeoylzyhitcs0vaPksDduvzW46HLjqXhpCg=
|
github.com/likexian/whois v1.15.3 h1:0emFSUSUj98Q12Wer3iM3eROPXjg+CyUBlibGPNbKHw=
|
||||||
github.com/likexian/whois v1.14.4/go.mod h1:ijMqjPbtafr5TB49U3Hs3BsJx/93ixhRi/8YnCSK0iY=
|
github.com/likexian/whois v1.15.3/go.mod h1:a6sGAAKEb+O3JRBuW2x/QDM80l5hJ07p0+SjQkJ1c+0=
|
||||||
github.com/likexian/whois-parser v1.24.2 h1:EaNwcXqWBfQPfLD5F6MxAA0o7FnzmGcN3mg7v7h0zfY=
|
github.com/likexian/whois v1.15.4 h1:r5En62c+S9HKFgJtdh2WsdmRGTcxE4WUtGBdZkSBXmM=
|
||||||
github.com/likexian/whois-parser v1.24.2/go.mod h1:fT0m/HCa4PXuR4ddA5PIE9V7gTWldeiMG/AHpeAhLtg=
|
github.com/likexian/whois v1.15.4/go.mod h1:rXFTPcQdNlPQBJCQpPWTSIDGzzmgKBftmhdOOcLpwXk=
|
||||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
github.com/likexian/whois-parser v1.24.18 h1:Xolieo/uwjNwhmQN/oDDNlwFajHipdHedyPBgzG44kw=
|
||||||
|
github.com/likexian/whois-parser v1.24.18/go.mod h1:k5zmKRZ7xPg1TLv3BGT4g/LOPRIMhvdNMeB0F53V/jk=
|
||||||
|
github.com/likexian/whois-parser v1.24.19 h1:vT8lWhnV8ogkdaYLyef6IvE5VTHVCwlUDG5BUXCx06k=
|
||||||
|
github.com/likexian/whois-parser v1.24.19/go.mod h1:rAtaofg2luol09H+ogDzGIfcG8ig1NtM5R16uQADDz4=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs=
|
github.com/oschwald/geoip2-golang v1.11.0 h1:hNENhCn1Uyzhf9PTmquXENiWS6AlxAEnBII6r8krA3w=
|
||||||
github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw=
|
github.com/oschwald/geoip2-golang v1.11.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo=
|
||||||
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
|
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
|
||||||
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
|
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
|
||||||
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/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
|
||||||
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.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
|
||||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||||
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
|
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||||
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||||
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
|
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||||
|
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||||
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
|
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
|
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||||
|
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
|
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||||
|
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
@ -4,3 +4,4 @@
|
|||||||
docker build -t dip:latest .
|
docker build -t dip:latest .
|
||||||
docker tag dip:latest registry.paulbsd.com/images/dip:latest
|
docker tag dip:latest registry.paulbsd.com/images/dip:latest
|
||||||
docker push registry.paulbsd.com/images/dip:latest
|
docker push registry.paulbsd.com/images/dip:latest
|
||||||
|
kubectl rollout restart deployment dip
|
@ -1,11 +1,14 @@
|
|||||||
package dip
|
package dip
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.paulbsd.com/paulbsd/dip/src/geoip"
|
"git.paulbsd.com/paulbsd/dip/src/geoip"
|
||||||
|
"github.com/bradfitz/gomemcache/memcache"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"github.com/likexian/whois"
|
"github.com/likexian/whois"
|
||||||
whoisparser "github.com/likexian/whois-parser"
|
whoisparser "github.com/likexian/whois-parser"
|
||||||
@ -13,26 +16,53 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const defaultLanguage = "en"
|
const defaultLanguage = "en"
|
||||||
|
const cacheMaxTime = 604800
|
||||||
|
|
||||||
var citydb *geoip2.Reader
|
var citydb *geoip2.Reader
|
||||||
var asndb *geoip2.Reader
|
var asndb *geoip2.Reader
|
||||||
|
var MemcacheConn string = ""
|
||||||
|
|
||||||
func Init() (err error) {
|
func Init() (err error) {
|
||||||
citydb, asndb, err = geoip.InitGeoIP()
|
citydb, asndb, err = geoip.InitGeoIP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
|
log.Println("failed to get geoip database")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetIPInfo returns IP address informations
|
// GetIPInfo returns IP address informations
|
||||||
func (ip *IP) GetIPInfo(c echo.Context) (err error) {
|
func (ip *IP) GetIPInfo(c echo.Context) (cached bool, err error) {
|
||||||
if c.Param("ip") != "" {
|
var mcenabled bool
|
||||||
ip.IP = c.Param("ip")
|
var mc *memcache.Client
|
||||||
} else {
|
if MemcacheConn != "" {
|
||||||
|
mc = memcache.New(MemcacheConn)
|
||||||
|
mc.Timeout, err = time.ParseDuration("1s")
|
||||||
|
mcenabled = true
|
||||||
|
defer mc.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
ip.IP = c.Param("ip")
|
||||||
|
if c.Param("ip") == "" {
|
||||||
ip.IP = c.RealIP()
|
ip.IP = c.RealIP()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if mcenabled {
|
||||||
|
item, err := mc.Get(ip.IP)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("error from cache", ip.IP, err)
|
||||||
|
cached = false
|
||||||
|
} else {
|
||||||
|
cachedip := IP{}
|
||||||
|
err = json.Unmarshal(item.Value, &cachedip)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
*ip = cachedip
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = ip.CheckIPAddress()
|
err = ip.CheckIPAddress()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ip.IP = c.RealIP()
|
ip.IP = c.RealIP()
|
||||||
@ -52,6 +82,16 @@ func (ip *IP) GetIPInfo(c echo.Context) (err error) {
|
|||||||
log.Println(err)
|
log.Println(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if mcenabled && !cached {
|
||||||
|
go func() {
|
||||||
|
dt, err := json.Marshal(*ip)
|
||||||
|
err = mc.Set(&memcache.Item{Key: ip.IP, Value: dt, Expiration: cacheMaxTime})
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err, "test")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
/*err = ip.GetWhois()
|
/*err = ip.GetWhois()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
|
@ -13,24 +13,30 @@ import (
|
|||||||
geoip2 "github.com/oschwald/geoip2-golang"
|
geoip2 "github.com/oschwald/geoip2-golang"
|
||||||
)
|
)
|
||||||
|
|
||||||
var RootURL = "https://git.paulbsd.com/paulbsd/GeoLite.mmdb/releases/download/%s/%s"
|
const RootURL = "https://git.paulbsd.com/paulbsd/GeoLite.mmdb/releases/download/%s/%s"
|
||||||
var APIUrl = "https://git.paulbsd.com/api/v1/repos/paulbsd/GeoLite.mmdb/releases"
|
const APIUrl = "https://git.paulbsd.com/api/v1/repos/paulbsd/GeoLite.mmdb/releases"
|
||||||
|
|
||||||
func GetLastVersion() string {
|
const CityFilename = "GeoLite2-City.mmdb"
|
||||||
|
const ASNFilename = "GeoLite2-ASN.mmdb"
|
||||||
|
|
||||||
|
func GetLastVersion() (result string, err error) {
|
||||||
var apiresults []struct {
|
var apiresults []struct {
|
||||||
Tag string `json:"tag_name"`
|
Tag string `json:"tag_name"`
|
||||||
}
|
}
|
||||||
res, err := http.Get(APIUrl)
|
res, err := http.Get(APIUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
body, err := io.ReadAll(res.Body)
|
body, err := io.ReadAll(res.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
err = json.Unmarshal(body, &apiresults)
|
err = json.Unmarshal(body, &apiresults)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var tags []string
|
var tags []string
|
||||||
@ -39,14 +45,18 @@ func GetLastVersion() string {
|
|||||||
}
|
}
|
||||||
sort.Sort(sort.Reverse(sort.StringSlice(tags)))
|
sort.Sort(sort.Reverse(sort.StringSlice(tags)))
|
||||||
|
|
||||||
return tags[0]
|
result = tags[0]
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitGeoIP() (citydb *geoip2.Reader, asndb *geoip2.Reader, err error) {
|
func InitGeoIP() (citydb *geoip2.Reader, asndb *geoip2.Reader, err error) {
|
||||||
var version = GetLastVersion()
|
version, err := GetLastVersion()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
var dbs map[string]string = map[string]string{
|
var dbs map[string]string = map[string]string{
|
||||||
"city": fmt.Sprintf(RootURL, version, "GeoLite2-City.mmdb"),
|
"city": fmt.Sprintf(RootURL, version, CityFilename),
|
||||||
"asn": fmt.Sprintf(RootURL, version, "GeoLite2-ASN.mmdb")}
|
"asn": fmt.Sprintf(RootURL, version, ASNFilename)}
|
||||||
log.Printf("Fetching GeoLite.mmdb version %s\n", version)
|
log.Printf("Fetching GeoLite.mmdb version %s\n", version)
|
||||||
citydb, err = FetchDB(dbs["city"], version)
|
citydb, err = FetchDB(dbs["city"], version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -21,7 +21,10 @@ func GetStatic(staticfiles *embed.FS, c echo.Context) (err error) {
|
|||||||
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJavaScript)
|
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJavaScript)
|
||||||
} else if strings.HasSuffix(name, ".css") {
|
} else if strings.HasSuffix(name, ".css") {
|
||||||
c.Response().Header().Set(echo.HeaderContentType, "text/css")
|
c.Response().Header().Set(echo.HeaderContentType, "text/css")
|
||||||
|
} else if strings.HasSuffix(name, ".ico") {
|
||||||
|
c.Response().Header().Set(echo.HeaderContentType, "image/x-icon")
|
||||||
}
|
}
|
||||||
|
c.Response().Header().Add(echo.HeaderCacheControl, "max-age=172800")
|
||||||
return c.String(http.StatusOK, string(content))
|
return c.String(http.StatusOK, string(content))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Con
|
|||||||
if strings.HasSuffix(name, ".html") {
|
if strings.HasSuffix(name, ".html") {
|
||||||
c.Response().Header().Set(echo.HeaderContentType, echo.MIMETextHTMLCharsetUTF8)
|
c.Response().Header().Set(echo.HeaderContentType, echo.MIMETextHTMLCharsetUTF8)
|
||||||
}
|
}
|
||||||
|
c.Response().Header().Add(echo.HeaderCacheControl, "max-age=172800")
|
||||||
return t.templates.ExecuteTemplate(w, name, data)
|
return t.templates.ExecuteTemplate(w, name, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,29 +39,6 @@ func BuildTemplates(templatefiles *embed.FS) (builttemplates *Template, err erro
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
// BuildTemplatesDir converts packr packages to html/template
|
|
||||||
func BuildTemplatesDir(dir string) (builttemplates *Template, err error) {
|
|
||||||
tmpl := template.New("templates")
|
|
||||||
|
|
||||||
err = pkger.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
freader, _ := pkger.Open(path)
|
|
||||||
tmplContent, err := ioutil.ReadAll(freader)
|
|
||||||
tmpl.New(info.Name()).Parse(string(tmplContent))
|
|
||||||
fmt.Println(info.Name(), tmplContent)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
|
|
||||||
builttemplates = &Template{
|
|
||||||
templates: tmpl,
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// Template is a template struct
|
// Template is a template struct
|
||||||
type Template struct {
|
type Template struct {
|
||||||
templates *template.Template
|
templates *template.Template
|
||||||
|
@ -22,7 +22,7 @@ func (ws *WS) InitServer(templatefiles *embed.FS, staticfiles *embed.FS) (err er
|
|||||||
}))
|
}))
|
||||||
ws.e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
|
ws.e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
|
||||||
AllowOrigins: []string{"*"},
|
AllowOrigins: []string{"*"},
|
||||||
AllowMethods: []string{http.MethodGet, http.MethodPut, http.MethodPost, http.MethodDelete},
|
AllowMethods: []string{http.MethodGet},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
builtTemplates, _ := templates.BuildTemplates(templatefiles)
|
builtTemplates, _ := templates.BuildTemplates(templatefiles)
|
||||||
@ -54,7 +54,12 @@ func (ws *WS) RunServer(config config.Config, templatefiles *embed.FS, staticfil
|
|||||||
ws.e.GET("/static/*", func(c echo.Context) (err error) {
|
ws.e.GET("/static/*", func(c echo.Context) (err error) {
|
||||||
return static.GetStatic(staticfiles, c)
|
return static.GetStatic(staticfiles, c)
|
||||||
})
|
})
|
||||||
|
ws.e.GET("/health", func(c echo.Context) (err error) {
|
||||||
|
return c.String(http.StatusOK, "OK")
|
||||||
|
})
|
||||||
|
ws.e.HEAD("/health", func(c echo.Context) (err error) {
|
||||||
|
return c.String(http.StatusOK, "OK")
|
||||||
|
})
|
||||||
ws.e.Logger.Fatal(ws.e.Start(fmt.Sprintf("%s:%s", config.Host, config.Port)))
|
ws.e.Logger.Fatal(ws.e.Start(fmt.Sprintf("%s:%s", config.Host, config.Port)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,9 @@ import (
|
|||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *Page) GetContent(c echo.Context) (err error) {
|
func (p *Page) GetContent(c echo.Context) (cached bool, err error) {
|
||||||
var ip dip.IP
|
var ip dip.IP
|
||||||
err = ip.GetIPInfo(c)
|
cached, err = ip.GetIPInfo(c)
|
||||||
p.IP = &ip
|
p.IP = &ip
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -18,7 +18,11 @@ func (p *Page) GetContent(c echo.Context) (err error) {
|
|||||||
|
|
||||||
// Process returns main webpage or a JSON
|
// Process returns main webpage or a JSON
|
||||||
func (p *Page) Process(c echo.Context, querytype string) (err error) {
|
func (p *Page) Process(c echo.Context, querytype string) (err error) {
|
||||||
p.GetContent(c)
|
cached, err := p.GetContent(c)
|
||||||
|
c.Response().Header().Set("X-Cached", "false")
|
||||||
|
if cached {
|
||||||
|
c.Response().Header().Set("X-Cached", "true")
|
||||||
|
}
|
||||||
if querytype == "json" {
|
if querytype == "json" {
|
||||||
return c.JSONPretty(http.StatusOK, p.IP, " ")
|
return c.JSONPretty(http.StatusOK, p.IP, " ")
|
||||||
}
|
}
|
||||||
|
BIN
static/images/favicon.ico
Normal file
BIN
static/images/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
3
static/js/axios.min.js
vendored
3
static/js/axios.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,29 +1,39 @@
|
|||||||
var vue = new Vue({
|
const dip_ip = document.getElementById("dip_ip");
|
||||||
el: '#dip_main_div',
|
const dip_hostname = document.getElementById("dip_hostname");
|
||||||
|
const dip_city = document.getElementById("dip_city");
|
||||||
|
const dip_country = document.getElementById("dip_country");
|
||||||
|
const dip_as_number = document.getElementById("dip_as_number");
|
||||||
|
const dip_as_org = document.getElementById("dip_as_org");
|
||||||
|
|
||||||
data () {
|
function setData(res) {
|
||||||
return {
|
dip_ip.innerHTML = res["ip"];
|
||||||
title: "Public IP Address service",
|
dip_hostname.innerHTML = res["hostname"];
|
||||||
dip: {"ip": null,
|
dip_city.innerHTML = res["city"];
|
||||||
"hostname": null,
|
dip_country.innerHTML = res["country"];
|
||||||
"city": null,
|
dip_as_number.innerHTML = res["as"]["number"];
|
||||||
"country": null},
|
dip_as_org.innerHTML = res["as"]["org"];
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
mounted () {
|
function updateIP() {
|
||||||
var ip = "";
|
let ip = "";
|
||||||
if (window.location.pathname.length > 4) {
|
|
||||||
ip = window.location.pathname.split("/")[1];
|
if (window.location.pathname.length > 4) {
|
||||||
console.log(ip);
|
ip = window.location.pathname.split("/")[1];
|
||||||
}
|
|
||||||
axios.get(`/json/${ip}`)
|
|
||||||
.then(response => {
|
|
||||||
this.dip = response.data
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
// Manage the state of the application if the request
|
|
||||||
// has failed
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
const localdata = localStorage.getItem("data");
|
||||||
|
if (localdata) {
|
||||||
|
const data = JSON.parse(localdata);
|
||||||
|
setData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(`/json/${ip}`).then((response) => {
|
||||||
|
response.json().then((data) => {
|
||||||
|
setData(data);
|
||||||
|
localStorage.setItem("data",JSON.stringify(data));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateIP()
|
||||||
|
setInterval(updateIP,1000);
|
||||||
|
6
static/js/vue.min.js
vendored
6
static/js/vue.min.js
vendored
File diff suppressed because one or more lines are too long
@ -8,4 +8,5 @@ import (
|
|||||||
//
|
//
|
||||||
//go:embed css/*
|
//go:embed css/*
|
||||||
//go:embed js/*
|
//go:embed js/*
|
||||||
|
//go:embed images/*
|
||||||
var Static embed.FS
|
var Static embed.FS
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>{{ .Title }}</title>
|
<title>{{ .Title }}</title>
|
||||||
|
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
|
<link rel="icon" href="static/images/favicon.ico"/>
|
||||||
<link rel="stylesheet" href="static/css/main.css" />
|
<link rel="stylesheet" href="static/css/main.css" />
|
||||||
<link rel="stylesheet" href="static/css/uikit.min.css" />
|
<link rel="stylesheet" href="static/css/uikit.min.css" />
|
||||||
<link rel="stylesheet" href="static/css/font-awesome.min.css" />
|
<link rel="stylesheet" href="static/css/font-awesome.min.css" />
|
||||||
|
|
||||||
<script src="static/js/uikit.min.js"></script>
|
<script src="static/js/uikit.min.js"></script>
|
||||||
<script src="static/js/uikit-icons.min.js"></script>
|
<script src="static/js/uikit-icons.min.js"></script>
|
||||||
<script src="static/js/axios.min.js"></script>
|
|
||||||
<script src="static/js/vue.min.js"></script>
|
|
||||||
</head>
|
</head>
|
||||||
|
@ -8,29 +8,27 @@
|
|||||||
<caption>IP informations</caption>
|
<caption>IP informations</caption>
|
||||||
<tr>
|
<tr>
|
||||||
<td>IP</td>
|
<td>IP</td>
|
||||||
<td>{{ "{{" }} dip.ip {{ "}}" }}</td>
|
<td id="dip_ip"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Reverse DNS</td>
|
<td>Reverse DNS</td>
|
||||||
<td>{{ "{{" }} dip.hostname {{ "}}" }}</td>
|
<td id="dip_hostname"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>City</td>
|
<td>City</td>
|
||||||
<td>{{ "{{" }} dip.city {{ "}}" }}</td>
|
<td id="dip_city"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Country</td>
|
<td>Country</td>
|
||||||
<td>{{ "{{" }} dip.country {{ "}}" }}</td>
|
<td id="dip_country"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>AS number</td>
|
<td>AS number</td>
|
||||||
<td>{{ "{{" }} dip.as.number {{ "}}" }}</td>
|
<td id="dip_as_number"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>AS name</td>
|
<td>AS name</td>
|
||||||
<td>
|
<td id="dip_as_org"></td>
|
||||||
{{ "{{" }} dip.as.org {{ "}}" }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</div>
|
</div>
|
||||||
{{ template "footer_js.html" .}}
|
{{ template "footer_js.html" .}}
|
||||||
|
9
vendor/github.com/bradfitz/gomemcache/AUTHORS
generated
vendored
Normal file
9
vendor/github.com/bradfitz/gomemcache/AUTHORS
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
The following people & companies are the copyright holders of this
|
||||||
|
package. Feel free to add to this list if you or your employer cares,
|
||||||
|
otherwise it's implicit from the git log.
|
||||||
|
|
||||||
|
Authors:
|
||||||
|
|
||||||
|
- Brad Fitzpatrick
|
||||||
|
- Google, Inc. (from Googlers contributing)
|
||||||
|
- Anybody else in the git log.
|
202
vendor/github.com/bradfitz/gomemcache/LICENSE
generated
vendored
Normal file
202
vendor/github.com/bradfitz/gomemcache/LICENSE
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
776
vendor/github.com/bradfitz/gomemcache/memcache/memcache.go
generated
vendored
Normal file
776
vendor/github.com/bradfitz/gomemcache/memcache/memcache.go
generated
vendored
Normal file
@ -0,0 +1,776 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2011 The gomemcache AUTHORS
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package memcache provides a client for the memcached cache server.
|
||||||
|
package memcache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Similar to:
|
||||||
|
// https://godoc.org/google.golang.org/appengine/memcache
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrCacheMiss means that a Get failed because the item wasn't present.
|
||||||
|
ErrCacheMiss = errors.New("memcache: cache miss")
|
||||||
|
|
||||||
|
// ErrCASConflict means that a CompareAndSwap call failed due to the
|
||||||
|
// cached value being modified between the Get and the CompareAndSwap.
|
||||||
|
// If the cached value was simply evicted rather than replaced,
|
||||||
|
// ErrNotStored will be returned instead.
|
||||||
|
ErrCASConflict = errors.New("memcache: compare-and-swap conflict")
|
||||||
|
|
||||||
|
// ErrNotStored means that a conditional write operation (i.e. Add or
|
||||||
|
// CompareAndSwap) failed because the condition was not satisfied.
|
||||||
|
ErrNotStored = errors.New("memcache: item not stored")
|
||||||
|
|
||||||
|
// ErrServer means that a server error occurred.
|
||||||
|
ErrServerError = errors.New("memcache: server error")
|
||||||
|
|
||||||
|
// ErrNoStats means that no statistics were available.
|
||||||
|
ErrNoStats = errors.New("memcache: no statistics available")
|
||||||
|
|
||||||
|
// ErrMalformedKey is returned when an invalid key is used.
|
||||||
|
// Keys must be at maximum 250 bytes long and not
|
||||||
|
// contain whitespace or control characters.
|
||||||
|
ErrMalformedKey = errors.New("malformed: key is too long or contains invalid characters")
|
||||||
|
|
||||||
|
// ErrNoServers is returned when no servers are configured or available.
|
||||||
|
ErrNoServers = errors.New("memcache: no servers configured or available")
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DefaultTimeout is the default socket read/write timeout.
|
||||||
|
DefaultTimeout = 500 * time.Millisecond
|
||||||
|
|
||||||
|
// DefaultMaxIdleConns is the default maximum number of idle connections
|
||||||
|
// kept for any single address.
|
||||||
|
DefaultMaxIdleConns = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
const buffered = 8 // arbitrary buffered channel size, for readability
|
||||||
|
|
||||||
|
// resumableError returns true if err is only a protocol-level cache error.
|
||||||
|
// This is used to determine whether or not a server connection should
|
||||||
|
// be re-used or not. If an error occurs, by default we don't reuse the
|
||||||
|
// connection, unless it was just a cache error.
|
||||||
|
func resumableError(err error) bool {
|
||||||
|
switch err {
|
||||||
|
case ErrCacheMiss, ErrCASConflict, ErrNotStored, ErrMalformedKey:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func legalKey(key string) bool {
|
||||||
|
if len(key) > 250 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := 0; i < len(key); i++ {
|
||||||
|
if key[i] <= ' ' || key[i] == 0x7f {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
crlf = []byte("\r\n")
|
||||||
|
space = []byte(" ")
|
||||||
|
resultOK = []byte("OK\r\n")
|
||||||
|
resultStored = []byte("STORED\r\n")
|
||||||
|
resultNotStored = []byte("NOT_STORED\r\n")
|
||||||
|
resultExists = []byte("EXISTS\r\n")
|
||||||
|
resultNotFound = []byte("NOT_FOUND\r\n")
|
||||||
|
resultDeleted = []byte("DELETED\r\n")
|
||||||
|
resultEnd = []byte("END\r\n")
|
||||||
|
resultOk = []byte("OK\r\n")
|
||||||
|
resultTouched = []byte("TOUCHED\r\n")
|
||||||
|
|
||||||
|
resultClientErrorPrefix = []byte("CLIENT_ERROR ")
|
||||||
|
versionPrefix = []byte("VERSION")
|
||||||
|
)
|
||||||
|
|
||||||
|
// New returns a memcache client using the provided server(s)
|
||||||
|
// with equal weight. If a server is listed multiple times,
|
||||||
|
// it gets a proportional amount of weight.
|
||||||
|
func New(server ...string) *Client {
|
||||||
|
ss := new(ServerList)
|
||||||
|
ss.SetServers(server...)
|
||||||
|
return NewFromSelector(ss)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFromSelector returns a new Client using the provided ServerSelector.
|
||||||
|
func NewFromSelector(ss ServerSelector) *Client {
|
||||||
|
return &Client{selector: ss}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client is a memcache client.
|
||||||
|
// It is safe for unlocked use by multiple concurrent goroutines.
|
||||||
|
type Client struct {
|
||||||
|
// DialContext connects to the address on the named network using the
|
||||||
|
// provided context.
|
||||||
|
//
|
||||||
|
// To connect to servers using TLS (memcached running with "--enable-ssl"),
|
||||||
|
// use a DialContext func that uses tls.Dialer.DialContext. See this
|
||||||
|
// package's tests as an example.
|
||||||
|
DialContext func(ctx context.Context, network, address string) (net.Conn, error)
|
||||||
|
|
||||||
|
// Timeout specifies the socket read/write timeout.
|
||||||
|
// If zero, DefaultTimeout is used.
|
||||||
|
Timeout time.Duration
|
||||||
|
|
||||||
|
// MaxIdleConns specifies the maximum number of idle connections that will
|
||||||
|
// be maintained per address. If less than one, DefaultMaxIdleConns will be
|
||||||
|
// used.
|
||||||
|
//
|
||||||
|
// Consider your expected traffic rates and latency carefully. This should
|
||||||
|
// be set to a number higher than your peak parallel requests.
|
||||||
|
MaxIdleConns int
|
||||||
|
|
||||||
|
selector ServerSelector
|
||||||
|
|
||||||
|
lk sync.Mutex
|
||||||
|
freeconn map[string][]*conn
|
||||||
|
}
|
||||||
|
|
||||||
|
// Item is an item to be got or stored in a memcached server.
|
||||||
|
type Item struct {
|
||||||
|
// Key is the Item's key (250 bytes maximum).
|
||||||
|
Key string
|
||||||
|
|
||||||
|
// Value is the Item's value.
|
||||||
|
Value []byte
|
||||||
|
|
||||||
|
// Flags are server-opaque flags whose semantics are entirely
|
||||||
|
// up to the app.
|
||||||
|
Flags uint32
|
||||||
|
|
||||||
|
// Expiration is the cache expiration time, in seconds: either a relative
|
||||||
|
// time from now (up to 1 month), or an absolute Unix epoch time.
|
||||||
|
// Zero means the Item has no expiration time.
|
||||||
|
Expiration int32
|
||||||
|
|
||||||
|
// CasID is the compare and swap ID.
|
||||||
|
//
|
||||||
|
// It's populated by get requests and then the same value is
|
||||||
|
// required for a CompareAndSwap request to succeed.
|
||||||
|
CasID uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// conn is a connection to a server.
|
||||||
|
type conn struct {
|
||||||
|
nc net.Conn
|
||||||
|
rw *bufio.ReadWriter
|
||||||
|
addr net.Addr
|
||||||
|
c *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// release returns this connection back to the client's free pool
|
||||||
|
func (cn *conn) release() {
|
||||||
|
cn.c.putFreeConn(cn.addr, cn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cn *conn) extendDeadline() {
|
||||||
|
cn.nc.SetDeadline(time.Now().Add(cn.c.netTimeout()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// condRelease releases this connection if the error pointed to by err
|
||||||
|
// is nil (not an error) or is only a protocol level error (e.g. a
|
||||||
|
// cache miss). The purpose is to not recycle TCP connections that
|
||||||
|
// are bad.
|
||||||
|
func (cn *conn) condRelease(err *error) {
|
||||||
|
if *err == nil || resumableError(*err) {
|
||||||
|
cn.release()
|
||||||
|
} else {
|
||||||
|
cn.nc.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) putFreeConn(addr net.Addr, cn *conn) {
|
||||||
|
c.lk.Lock()
|
||||||
|
defer c.lk.Unlock()
|
||||||
|
if c.freeconn == nil {
|
||||||
|
c.freeconn = make(map[string][]*conn)
|
||||||
|
}
|
||||||
|
freelist := c.freeconn[addr.String()]
|
||||||
|
if len(freelist) >= c.maxIdleConns() {
|
||||||
|
cn.nc.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.freeconn[addr.String()] = append(freelist, cn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) getFreeConn(addr net.Addr) (cn *conn, ok bool) {
|
||||||
|
c.lk.Lock()
|
||||||
|
defer c.lk.Unlock()
|
||||||
|
if c.freeconn == nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
freelist, ok := c.freeconn[addr.String()]
|
||||||
|
if !ok || len(freelist) == 0 {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
cn = freelist[len(freelist)-1]
|
||||||
|
c.freeconn[addr.String()] = freelist[:len(freelist)-1]
|
||||||
|
return cn, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) netTimeout() time.Duration {
|
||||||
|
if c.Timeout != 0 {
|
||||||
|
return c.Timeout
|
||||||
|
}
|
||||||
|
return DefaultTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) maxIdleConns() int {
|
||||||
|
if c.MaxIdleConns > 0 {
|
||||||
|
return c.MaxIdleConns
|
||||||
|
}
|
||||||
|
return DefaultMaxIdleConns
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectTimeoutError is the error type used when it takes
|
||||||
|
// too long to connect to the desired host. This level of
|
||||||
|
// detail can generally be ignored.
|
||||||
|
type ConnectTimeoutError struct {
|
||||||
|
Addr net.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cte *ConnectTimeoutError) Error() string {
|
||||||
|
return "memcache: connect timeout to " + cte.Addr.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) dial(addr net.Addr) (net.Conn, error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), c.netTimeout())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
dialerContext := c.DialContext
|
||||||
|
if dialerContext == nil {
|
||||||
|
dialer := net.Dialer{
|
||||||
|
Timeout: c.netTimeout(),
|
||||||
|
}
|
||||||
|
dialerContext = dialer.DialContext
|
||||||
|
}
|
||||||
|
|
||||||
|
nc, err := dialerContext(ctx, addr.Network(), addr.String())
|
||||||
|
if err == nil {
|
||||||
|
return nc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if ne, ok := err.(net.Error); ok && ne.Timeout() {
|
||||||
|
return nil, &ConnectTimeoutError{addr}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) getConn(addr net.Addr) (*conn, error) {
|
||||||
|
cn, ok := c.getFreeConn(addr)
|
||||||
|
if ok {
|
||||||
|
cn.extendDeadline()
|
||||||
|
return cn, nil
|
||||||
|
}
|
||||||
|
nc, err := c.dial(addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cn = &conn{
|
||||||
|
nc: nc,
|
||||||
|
addr: addr,
|
||||||
|
rw: bufio.NewReadWriter(bufio.NewReader(nc), bufio.NewWriter(nc)),
|
||||||
|
c: c,
|
||||||
|
}
|
||||||
|
cn.extendDeadline()
|
||||||
|
return cn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) onItem(item *Item, fn func(*Client, *bufio.ReadWriter, *Item) error) error {
|
||||||
|
addr, err := c.selector.PickServer(item.Key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cn, err := c.getConn(addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer cn.condRelease(&err)
|
||||||
|
if err = fn(c, cn.rw, item); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) FlushAll() error {
|
||||||
|
return c.selector.Each(c.flushAllFromAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get gets the item for the given key. ErrCacheMiss is returned for a
|
||||||
|
// memcache cache miss. The key must be at most 250 bytes in length.
|
||||||
|
func (c *Client) Get(key string) (item *Item, err error) {
|
||||||
|
err = c.withKeyAddr(key, func(addr net.Addr) error {
|
||||||
|
return c.getFromAddr(addr, []string{key}, func(it *Item) { item = it })
|
||||||
|
})
|
||||||
|
if err == nil && item == nil {
|
||||||
|
err = ErrCacheMiss
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Touch updates the expiry for the given key. The seconds parameter is either
|
||||||
|
// a Unix timestamp or, if seconds is less than 1 month, the number of seconds
|
||||||
|
// into the future at which time the item will expire. Zero means the item has
|
||||||
|
// no expiration time. ErrCacheMiss is returned if the key is not in the cache.
|
||||||
|
// The key must be at most 250 bytes in length.
|
||||||
|
func (c *Client) Touch(key string, seconds int32) (err error) {
|
||||||
|
return c.withKeyAddr(key, func(addr net.Addr) error {
|
||||||
|
return c.touchFromAddr(addr, []string{key}, seconds)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) withKeyAddr(key string, fn func(net.Addr) error) (err error) {
|
||||||
|
if !legalKey(key) {
|
||||||
|
return ErrMalformedKey
|
||||||
|
}
|
||||||
|
addr, err := c.selector.PickServer(key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return fn(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) withAddrRw(addr net.Addr, fn func(*bufio.ReadWriter) error) (err error) {
|
||||||
|
cn, err := c.getConn(addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer cn.condRelease(&err)
|
||||||
|
return fn(cn.rw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) withKeyRw(key string, fn func(*bufio.ReadWriter) error) error {
|
||||||
|
return c.withKeyAddr(key, func(addr net.Addr) error {
|
||||||
|
return c.withAddrRw(addr, fn)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) getFromAddr(addr net.Addr, keys []string, cb func(*Item)) error {
|
||||||
|
return c.withAddrRw(addr, func(rw *bufio.ReadWriter) error {
|
||||||
|
if _, err := fmt.Fprintf(rw, "gets %s\r\n", strings.Join(keys, " ")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := rw.Flush(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := parseGetResponse(rw.Reader, cb); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// flushAllFromAddr send the flush_all command to the given addr
|
||||||
|
func (c *Client) flushAllFromAddr(addr net.Addr) error {
|
||||||
|
return c.withAddrRw(addr, func(rw *bufio.ReadWriter) error {
|
||||||
|
if _, err := fmt.Fprintf(rw, "flush_all\r\n"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := rw.Flush(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
line, err := rw.ReadSlice('\n')
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case bytes.Equal(line, resultOk):
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("memcache: unexpected response line from flush_all: %q", string(line))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ping sends the version command to the given addr
|
||||||
|
func (c *Client) ping(addr net.Addr) error {
|
||||||
|
return c.withAddrRw(addr, func(rw *bufio.ReadWriter) error {
|
||||||
|
if _, err := fmt.Fprintf(rw, "version\r\n"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := rw.Flush(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
line, err := rw.ReadSlice('\n')
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case bytes.HasPrefix(line, versionPrefix):
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("memcache: unexpected response line from ping: %q", string(line))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) touchFromAddr(addr net.Addr, keys []string, expiration int32) error {
|
||||||
|
return c.withAddrRw(addr, func(rw *bufio.ReadWriter) error {
|
||||||
|
for _, key := range keys {
|
||||||
|
if _, err := fmt.Fprintf(rw, "touch %s %d\r\n", key, expiration); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := rw.Flush(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
line, err := rw.ReadSlice('\n')
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case bytes.Equal(line, resultTouched):
|
||||||
|
break
|
||||||
|
case bytes.Equal(line, resultNotFound):
|
||||||
|
return ErrCacheMiss
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("memcache: unexpected response line from touch: %q", string(line))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMulti is a batch version of Get. The returned map from keys to
|
||||||
|
// items may have fewer elements than the input slice, due to memcache
|
||||||
|
// cache misses. Each key must be at most 250 bytes in length.
|
||||||
|
// If no error is returned, the returned map will also be non-nil.
|
||||||
|
func (c *Client) GetMulti(keys []string) (map[string]*Item, error) {
|
||||||
|
var lk sync.Mutex
|
||||||
|
m := make(map[string]*Item)
|
||||||
|
addItemToMap := func(it *Item) {
|
||||||
|
lk.Lock()
|
||||||
|
defer lk.Unlock()
|
||||||
|
m[it.Key] = it
|
||||||
|
}
|
||||||
|
|
||||||
|
keyMap := make(map[net.Addr][]string)
|
||||||
|
for _, key := range keys {
|
||||||
|
if !legalKey(key) {
|
||||||
|
return nil, ErrMalformedKey
|
||||||
|
}
|
||||||
|
addr, err := c.selector.PickServer(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
keyMap[addr] = append(keyMap[addr], key)
|
||||||
|
}
|
||||||
|
|
||||||
|
ch := make(chan error, buffered)
|
||||||
|
for addr, keys := range keyMap {
|
||||||
|
go func(addr net.Addr, keys []string) {
|
||||||
|
ch <- c.getFromAddr(addr, keys, addItemToMap)
|
||||||
|
}(addr, keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
for _ = range keyMap {
|
||||||
|
if ge := <-ch; ge != nil {
|
||||||
|
err = ge
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseGetResponse reads a GET response from r and calls cb for each
|
||||||
|
// read and allocated Item
|
||||||
|
func parseGetResponse(r *bufio.Reader, cb func(*Item)) error {
|
||||||
|
for {
|
||||||
|
line, err := r.ReadSlice('\n')
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if bytes.Equal(line, resultEnd) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
it := new(Item)
|
||||||
|
size, err := scanGetResponseLine(line, it)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
it.Value = make([]byte, size+2)
|
||||||
|
_, err = io.ReadFull(r, it.Value)
|
||||||
|
if err != nil {
|
||||||
|
it.Value = nil
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !bytes.HasSuffix(it.Value, crlf) {
|
||||||
|
it.Value = nil
|
||||||
|
return fmt.Errorf("memcache: corrupt get result read")
|
||||||
|
}
|
||||||
|
it.Value = it.Value[:size]
|
||||||
|
cb(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// scanGetResponseLine populates it and returns the declared size of the item.
|
||||||
|
// It does not read the bytes of the item.
|
||||||
|
func scanGetResponseLine(line []byte, it *Item) (size int, err error) {
|
||||||
|
pattern := "VALUE %s %d %d %d\r\n"
|
||||||
|
dest := []interface{}{&it.Key, &it.Flags, &size, &it.CasID}
|
||||||
|
if bytes.Count(line, space) == 3 {
|
||||||
|
pattern = "VALUE %s %d %d\r\n"
|
||||||
|
dest = dest[:3]
|
||||||
|
}
|
||||||
|
n, err := fmt.Sscanf(string(line), pattern, dest...)
|
||||||
|
if err != nil || n != len(dest) {
|
||||||
|
return -1, fmt.Errorf("memcache: unexpected line in get response: %q", line)
|
||||||
|
}
|
||||||
|
return size, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set writes the given item, unconditionally.
|
||||||
|
func (c *Client) Set(item *Item) error {
|
||||||
|
return c.onItem(item, (*Client).set)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) set(rw *bufio.ReadWriter, item *Item) error {
|
||||||
|
return c.populateOne(rw, "set", item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add writes the given item, if no value already exists for its
|
||||||
|
// key. ErrNotStored is returned if that condition is not met.
|
||||||
|
func (c *Client) Add(item *Item) error {
|
||||||
|
return c.onItem(item, (*Client).add)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) add(rw *bufio.ReadWriter, item *Item) error {
|
||||||
|
return c.populateOne(rw, "add", item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace writes the given item, but only if the server *does*
|
||||||
|
// already hold data for this key
|
||||||
|
func (c *Client) Replace(item *Item) error {
|
||||||
|
return c.onItem(item, (*Client).replace)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) replace(rw *bufio.ReadWriter, item *Item) error {
|
||||||
|
return c.populateOne(rw, "replace", item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append appends the given item to the existing item, if a value already
|
||||||
|
// exists for its key. ErrNotStored is returned if that condition is not met.
|
||||||
|
func (c *Client) Append(item *Item) error {
|
||||||
|
return c.onItem(item, (*Client).append)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) append(rw *bufio.ReadWriter, item *Item) error {
|
||||||
|
return c.populateOne(rw, "append", item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepend prepends the given item to the existing item, if a value already
|
||||||
|
// exists for its key. ErrNotStored is returned if that condition is not met.
|
||||||
|
func (c *Client) Prepend(item *Item) error {
|
||||||
|
return c.onItem(item, (*Client).prepend)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) prepend(rw *bufio.ReadWriter, item *Item) error {
|
||||||
|
return c.populateOne(rw, "prepend", item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareAndSwap writes the given item that was previously returned
|
||||||
|
// by Get, if the value was neither modified or evicted between the
|
||||||
|
// Get and the CompareAndSwap calls. The item's Key should not change
|
||||||
|
// between calls but all other item fields may differ. ErrCASConflict
|
||||||
|
// is returned if the value was modified in between the
|
||||||
|
// calls. ErrNotStored is returned if the value was evicted in between
|
||||||
|
// the calls.
|
||||||
|
func (c *Client) CompareAndSwap(item *Item) error {
|
||||||
|
return c.onItem(item, (*Client).cas)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) cas(rw *bufio.ReadWriter, item *Item) error {
|
||||||
|
return c.populateOne(rw, "cas", item)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) populateOne(rw *bufio.ReadWriter, verb string, item *Item) error {
|
||||||
|
if !legalKey(item.Key) {
|
||||||
|
return ErrMalformedKey
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
if verb == "cas" {
|
||||||
|
_, err = fmt.Fprintf(rw, "%s %s %d %d %d %d\r\n",
|
||||||
|
verb, item.Key, item.Flags, item.Expiration, len(item.Value), item.CasID)
|
||||||
|
} else {
|
||||||
|
_, err = fmt.Fprintf(rw, "%s %s %d %d %d\r\n",
|
||||||
|
verb, item.Key, item.Flags, item.Expiration, len(item.Value))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err = rw.Write(item.Value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := rw.Write(crlf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := rw.Flush(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
line, err := rw.ReadSlice('\n')
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case bytes.Equal(line, resultStored):
|
||||||
|
return nil
|
||||||
|
case bytes.Equal(line, resultNotStored):
|
||||||
|
return ErrNotStored
|
||||||
|
case bytes.Equal(line, resultExists):
|
||||||
|
return ErrCASConflict
|
||||||
|
case bytes.Equal(line, resultNotFound):
|
||||||
|
return ErrCacheMiss
|
||||||
|
}
|
||||||
|
return fmt.Errorf("memcache: unexpected response line from %q: %q", verb, string(line))
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeReadLine(rw *bufio.ReadWriter, format string, args ...interface{}) ([]byte, error) {
|
||||||
|
_, err := fmt.Fprintf(rw, format, args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := rw.Flush(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
line, err := rw.ReadSlice('\n')
|
||||||
|
return line, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeExpectf(rw *bufio.ReadWriter, expect []byte, format string, args ...interface{}) error {
|
||||||
|
line, err := writeReadLine(rw, format, args...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case bytes.Equal(line, resultOK):
|
||||||
|
return nil
|
||||||
|
case bytes.Equal(line, expect):
|
||||||
|
return nil
|
||||||
|
case bytes.Equal(line, resultNotStored):
|
||||||
|
return ErrNotStored
|
||||||
|
case bytes.Equal(line, resultExists):
|
||||||
|
return ErrCASConflict
|
||||||
|
case bytes.Equal(line, resultNotFound):
|
||||||
|
return ErrCacheMiss
|
||||||
|
}
|
||||||
|
return fmt.Errorf("memcache: unexpected response line: %q", string(line))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes the item with the provided key. The error ErrCacheMiss is
|
||||||
|
// returned if the item didn't already exist in the cache.
|
||||||
|
func (c *Client) Delete(key string) error {
|
||||||
|
return c.withKeyRw(key, func(rw *bufio.ReadWriter) error {
|
||||||
|
return writeExpectf(rw, resultDeleted, "delete %s\r\n", key)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteAll deletes all items in the cache.
|
||||||
|
func (c *Client) DeleteAll() error {
|
||||||
|
return c.withKeyRw("", func(rw *bufio.ReadWriter) error {
|
||||||
|
return writeExpectf(rw, resultDeleted, "flush_all\r\n")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ping checks all instances if they are alive. Returns error if any
|
||||||
|
// of them is down.
|
||||||
|
func (c *Client) Ping() error {
|
||||||
|
return c.selector.Each(c.ping)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment atomically increments key by delta. The return value is
|
||||||
|
// the new value after being incremented or an error. If the value
|
||||||
|
// didn't exist in memcached the error is ErrCacheMiss. The value in
|
||||||
|
// memcached must be an decimal number, or an error will be returned.
|
||||||
|
// On 64-bit overflow, the new value wraps around.
|
||||||
|
func (c *Client) Increment(key string, delta uint64) (newValue uint64, err error) {
|
||||||
|
return c.incrDecr("incr", key, delta)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrement atomically decrements key by delta. The return value is
|
||||||
|
// the new value after being decremented or an error. If the value
|
||||||
|
// didn't exist in memcached the error is ErrCacheMiss. The value in
|
||||||
|
// memcached must be an decimal number, or an error will be returned.
|
||||||
|
// On underflow, the new value is capped at zero and does not wrap
|
||||||
|
// around.
|
||||||
|
func (c *Client) Decrement(key string, delta uint64) (newValue uint64, err error) {
|
||||||
|
return c.incrDecr("decr", key, delta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) incrDecr(verb, key string, delta uint64) (uint64, error) {
|
||||||
|
var val uint64
|
||||||
|
err := c.withKeyRw(key, func(rw *bufio.ReadWriter) error {
|
||||||
|
line, err := writeReadLine(rw, "%s %s %d\r\n", verb, key, delta)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case bytes.Equal(line, resultNotFound):
|
||||||
|
return ErrCacheMiss
|
||||||
|
case bytes.HasPrefix(line, resultClientErrorPrefix):
|
||||||
|
errMsg := line[len(resultClientErrorPrefix) : len(line)-2]
|
||||||
|
return errors.New("memcache: client error: " + string(errMsg))
|
||||||
|
}
|
||||||
|
val, err = strconv.ParseUint(string(line[:len(line)-2]), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return val, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes any open connections.
|
||||||
|
//
|
||||||
|
// It returns the first error encountered closing connections, but always
|
||||||
|
// closes all connections.
|
||||||
|
//
|
||||||
|
// After Close, the Client may still be used.
|
||||||
|
func (c *Client) Close() error {
|
||||||
|
c.lk.Lock()
|
||||||
|
defer c.lk.Unlock()
|
||||||
|
var ret error
|
||||||
|
for _, conns := range c.freeconn {
|
||||||
|
for _, c := range conns {
|
||||||
|
if err := c.nc.Close(); err != nil && ret == nil {
|
||||||
|
ret = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.freeconn = nil
|
||||||
|
return ret
|
||||||
|
}
|
129
vendor/github.com/bradfitz/gomemcache/memcache/selector.go
generated
vendored
Normal file
129
vendor/github.com/bradfitz/gomemcache/memcache/selector.go
generated
vendored
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2011 The gomemcache AUTHORS
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package memcache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"hash/crc32"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServerSelector is the interface that selects a memcache server
|
||||||
|
// as a function of the item's key.
|
||||||
|
//
|
||||||
|
// All ServerSelector implementations must be safe for concurrent use
|
||||||
|
// by multiple goroutines.
|
||||||
|
type ServerSelector interface {
|
||||||
|
// PickServer returns the server address that a given item
|
||||||
|
// should be shared onto.
|
||||||
|
PickServer(key string) (net.Addr, error)
|
||||||
|
Each(func(net.Addr) error) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerList is a simple ServerSelector. Its zero value is usable.
|
||||||
|
type ServerList struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
addrs []net.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
// staticAddr caches the Network() and String() values from any net.Addr.
|
||||||
|
type staticAddr struct {
|
||||||
|
ntw, str string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStaticAddr(a net.Addr) net.Addr {
|
||||||
|
return &staticAddr{
|
||||||
|
ntw: a.Network(),
|
||||||
|
str: a.String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *staticAddr) Network() string { return s.ntw }
|
||||||
|
func (s *staticAddr) String() string { return s.str }
|
||||||
|
|
||||||
|
// SetServers changes a ServerList's set of servers at runtime and is
|
||||||
|
// safe for concurrent use by multiple goroutines.
|
||||||
|
//
|
||||||
|
// Each server is given equal weight. A server is given more weight
|
||||||
|
// if it's listed multiple times.
|
||||||
|
//
|
||||||
|
// SetServers returns an error if any of the server names fail to
|
||||||
|
// resolve. No attempt is made to connect to the server. If any error
|
||||||
|
// is returned, no changes are made to the ServerList.
|
||||||
|
func (ss *ServerList) SetServers(servers ...string) error {
|
||||||
|
naddr := make([]net.Addr, len(servers))
|
||||||
|
for i, server := range servers {
|
||||||
|
if strings.Contains(server, "/") {
|
||||||
|
addr, err := net.ResolveUnixAddr("unix", server)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
naddr[i] = newStaticAddr(addr)
|
||||||
|
} else {
|
||||||
|
tcpaddr, err := net.ResolveTCPAddr("tcp", server)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
naddr[i] = newStaticAddr(tcpaddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ss.mu.Lock()
|
||||||
|
defer ss.mu.Unlock()
|
||||||
|
ss.addrs = naddr
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each iterates over each server calling the given function
|
||||||
|
func (ss *ServerList) Each(f func(net.Addr) error) error {
|
||||||
|
ss.mu.RLock()
|
||||||
|
defer ss.mu.RUnlock()
|
||||||
|
for _, a := range ss.addrs {
|
||||||
|
if err := f(a); nil != err {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// keyBufPool returns []byte buffers for use by PickServer's call to
|
||||||
|
// crc32.ChecksumIEEE to avoid allocations. (but doesn't avoid the
|
||||||
|
// copies, which at least are bounded in size and small)
|
||||||
|
var keyBufPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
b := make([]byte, 256)
|
||||||
|
return &b
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *ServerList) PickServer(key string) (net.Addr, error) {
|
||||||
|
ss.mu.RLock()
|
||||||
|
defer ss.mu.RUnlock()
|
||||||
|
if len(ss.addrs) == 0 {
|
||||||
|
return nil, ErrNoServers
|
||||||
|
}
|
||||||
|
if len(ss.addrs) == 1 {
|
||||||
|
return ss.addrs[0], nil
|
||||||
|
}
|
||||||
|
bufp := keyBufPool.Get().(*[]byte)
|
||||||
|
n := copy(*bufp, key)
|
||||||
|
cs := crc32.ChecksumIEEE((*bufp)[:n])
|
||||||
|
keyBufPool.Put(bufp)
|
||||||
|
|
||||||
|
return ss.addrs[cs%uint32(len(ss.addrs))], nil
|
||||||
|
}
|
21
vendor/github.com/labstack/echo/v4/.travis.yml
generated
vendored
21
vendor/github.com/labstack/echo/v4/.travis.yml
generated
vendored
@ -1,21 +0,0 @@
|
|||||||
arch:
|
|
||||||
- amd64
|
|
||||||
- ppc64le
|
|
||||||
|
|
||||||
language: go
|
|
||||||
go:
|
|
||||||
- 1.14.x
|
|
||||||
- 1.15.x
|
|
||||||
- tip
|
|
||||||
env:
|
|
||||||
- GO111MODULE=on
|
|
||||||
install:
|
|
||||||
- go get -v golang.org/x/lint/golint
|
|
||||||
script:
|
|
||||||
- golint -set_exit_status ./...
|
|
||||||
- go test -race -coverprofile=coverage.txt -covermode=atomic ./...
|
|
||||||
after_success:
|
|
||||||
- bash <(curl -s https://codecov.io/bash)
|
|
||||||
matrix:
|
|
||||||
allow_failures:
|
|
||||||
- go: tip
|
|
132
vendor/github.com/labstack/echo/v4/CHANGELOG.md
generated
vendored
132
vendor/github.com/labstack/echo/v4/CHANGELOG.md
generated
vendored
@ -1,5 +1,137 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v4.12.0 - 2024-04-15
|
||||||
|
|
||||||
|
**Security**
|
||||||
|
|
||||||
|
* Update golang.org/x/net dep because of [GO-2024-2687](https://pkg.go.dev/vuln/GO-2024-2687) by @aldas in https://github.com/labstack/echo/pull/2625
|
||||||
|
|
||||||
|
|
||||||
|
**Enhancements**
|
||||||
|
|
||||||
|
* binder: make binding to Map work better with string destinations by @aldas in https://github.com/labstack/echo/pull/2554
|
||||||
|
* README.md: add Encore as sponsor by @marcuskohlberg in https://github.com/labstack/echo/pull/2579
|
||||||
|
* Reorder paragraphs in README.md by @aldas in https://github.com/labstack/echo/pull/2581
|
||||||
|
* CI: upgrade actions/checkout to v4 by @aldas in https://github.com/labstack/echo/pull/2584
|
||||||
|
* Remove default charset from 'application/json' Content-Type header by @doortts in https://github.com/labstack/echo/pull/2568
|
||||||
|
* CI: Use Go 1.22 by @aldas in https://github.com/labstack/echo/pull/2588
|
||||||
|
* binder: allow binding to a nil map by @georgmu in https://github.com/labstack/echo/pull/2574
|
||||||
|
* Add Skipper Unit Test In BasicBasicAuthConfig and Add More Detail Explanation regarding BasicAuthValidator by @RyoKusnadi in https://github.com/labstack/echo/pull/2461
|
||||||
|
* fix some typos by @teslaedison in https://github.com/labstack/echo/pull/2603
|
||||||
|
* fix: some typos by @pomadev in https://github.com/labstack/echo/pull/2596
|
||||||
|
* Allow ResponseWriters to unwrap writers when flushing/hijacking by @aldas in https://github.com/labstack/echo/pull/2595
|
||||||
|
* Add SPDX licence comments to files. by @aldas in https://github.com/labstack/echo/pull/2604
|
||||||
|
* Upgrade deps by @aldas in https://github.com/labstack/echo/pull/2605
|
||||||
|
* Change type definition blocks to single declarations. This helps copy… by @aldas in https://github.com/labstack/echo/pull/2606
|
||||||
|
* Fix Real IP logic by @cl-bvl in https://github.com/labstack/echo/pull/2550
|
||||||
|
* Default binder can use `UnmarshalParams(params []string) error` inter… by @aldas in https://github.com/labstack/echo/pull/2607
|
||||||
|
* Default binder can bind pointer to slice as struct field. For example `*[]string` by @aldas in https://github.com/labstack/echo/pull/2608
|
||||||
|
* Remove maxparam dependence from Context by @aldas in https://github.com/labstack/echo/pull/2611
|
||||||
|
* When route is registered with empty path it is normalized to `/`. by @aldas in https://github.com/labstack/echo/pull/2616
|
||||||
|
* proxy middleware should use httputil.ReverseProxy for SSE requests by @aldas in https://github.com/labstack/echo/pull/2624
|
||||||
|
|
||||||
|
|
||||||
|
## v4.11.4 - 2023-12-20
|
||||||
|
|
||||||
|
**Security**
|
||||||
|
|
||||||
|
* Upgrade golang.org/x/crypto to v0.17.0 to fix vulnerability [issue](https://pkg.go.dev/vuln/GO-2023-2402) [#2562](https://github.com/labstack/echo/pull/2562)
|
||||||
|
|
||||||
|
**Enhancements**
|
||||||
|
|
||||||
|
* Update deps and mark Go version to 1.18 as this is what golang.org/x/* use [#2563](https://github.com/labstack/echo/pull/2563)
|
||||||
|
* Request logger: add example for Slog https://pkg.go.dev/log/slog [#2543](https://github.com/labstack/echo/pull/2543)
|
||||||
|
|
||||||
|
|
||||||
|
## v4.11.3 - 2023-11-07
|
||||||
|
|
||||||
|
**Security**
|
||||||
|
|
||||||
|
* 'c.Attachment' and 'c.Inline' should escape filename in 'Content-Disposition' header to avoid 'Reflect File Download' vulnerability. [#2541](https://github.com/labstack/echo/pull/2541)
|
||||||
|
|
||||||
|
**Enhancements**
|
||||||
|
|
||||||
|
* Tests: refactor context tests to be separate functions [#2540](https://github.com/labstack/echo/pull/2540)
|
||||||
|
* Proxy middleware: reuse echo request context [#2537](https://github.com/labstack/echo/pull/2537)
|
||||||
|
* Mark unmarshallable yaml struct tags as ignored [#2536](https://github.com/labstack/echo/pull/2536)
|
||||||
|
|
||||||
|
|
||||||
|
## v4.11.2 - 2023-10-11
|
||||||
|
|
||||||
|
**Security**
|
||||||
|
|
||||||
|
* Bump golang.org/x/net to prevent CVE-2023-39325 / CVE-2023-44487 HTTP/2 Rapid Reset Attack [#2527](https://github.com/labstack/echo/pull/2527)
|
||||||
|
* fix(sec): randomString bias introduced by #2490 [#2492](https://github.com/labstack/echo/pull/2492)
|
||||||
|
* CSRF/RequestID mw: switch math/random usage to crypto/random [#2490](https://github.com/labstack/echo/pull/2490)
|
||||||
|
|
||||||
|
**Enhancements**
|
||||||
|
|
||||||
|
* Delete unused context in body_limit.go [#2483](https://github.com/labstack/echo/pull/2483)
|
||||||
|
* Use Go 1.21 in CI [#2505](https://github.com/labstack/echo/pull/2505)
|
||||||
|
* Fix some typos [#2511](https://github.com/labstack/echo/pull/2511)
|
||||||
|
* Allow CORS middleware to send Access-Control-Max-Age: 0 [#2518](https://github.com/labstack/echo/pull/2518)
|
||||||
|
* Bump dependancies [#2522](https://github.com/labstack/echo/pull/2522)
|
||||||
|
|
||||||
|
## v4.11.1 - 2023-07-16
|
||||||
|
|
||||||
|
**Fixes**
|
||||||
|
|
||||||
|
* Fix `Gzip` middleware not sending response code for no content responses (404, 301/302 redirects etc) [#2481](https://github.com/labstack/echo/pull/2481)
|
||||||
|
|
||||||
|
|
||||||
|
## v4.11.0 - 2023-07-14
|
||||||
|
|
||||||
|
|
||||||
|
**Fixes**
|
||||||
|
|
||||||
|
* Fixes the proxy middleware concurrency issue of calling the Next() proxy target on Round Robin Balancer [#2409](https://github.com/labstack/echo/pull/2409)
|
||||||
|
* Fix `group.RouteNotFound` not working when group has attached middlewares [#2411](https://github.com/labstack/echo/pull/2411)
|
||||||
|
* Fix global error handler return error message when message is an error [#2456](https://github.com/labstack/echo/pull/2456)
|
||||||
|
* Do not use global timeNow variables [#2477](https://github.com/labstack/echo/pull/2477)
|
||||||
|
|
||||||
|
|
||||||
|
**Enhancements**
|
||||||
|
|
||||||
|
* Added a optional config variable to disable centralized error handler in recovery middleware [#2410](https://github.com/labstack/echo/pull/2410)
|
||||||
|
* refactor: use `strings.ReplaceAll` directly [#2424](https://github.com/labstack/echo/pull/2424)
|
||||||
|
* Add support for Go1.20 `http.rwUnwrapper` to Response struct [#2425](https://github.com/labstack/echo/pull/2425)
|
||||||
|
* Check whether is nil before invoking centralized error handling [#2429](https://github.com/labstack/echo/pull/2429)
|
||||||
|
* Proper colon support in `echo.Reverse` method [#2416](https://github.com/labstack/echo/pull/2416)
|
||||||
|
* Fix misuses of a vs an in documentation comments [#2436](https://github.com/labstack/echo/pull/2436)
|
||||||
|
* Add link to slog.Handler library for Echo logging into README.md [#2444](https://github.com/labstack/echo/pull/2444)
|
||||||
|
* In proxy middleware Support retries of failed proxy requests [#2414](https://github.com/labstack/echo/pull/2414)
|
||||||
|
* gofmt fixes to comments [#2452](https://github.com/labstack/echo/pull/2452)
|
||||||
|
* gzip response only if it exceeds a minimal length [#2267](https://github.com/labstack/echo/pull/2267)
|
||||||
|
* Upgrade packages [#2475](https://github.com/labstack/echo/pull/2475)
|
||||||
|
|
||||||
|
|
||||||
|
## v4.10.2 - 2023-02-22
|
||||||
|
|
||||||
|
**Security**
|
||||||
|
|
||||||
|
* `filepath.Clean` behaviour has changed in Go 1.20 - adapt to it [#2406](https://github.com/labstack/echo/pull/2406)
|
||||||
|
* Add `middleware.CORSConfig.UnsafeWildcardOriginWithAllowCredentials` to make UNSAFE usages of wildcard origin + allow cretentials less likely [#2405](https://github.com/labstack/echo/pull/2405)
|
||||||
|
|
||||||
|
**Enhancements**
|
||||||
|
|
||||||
|
* Add more HTTP error values [#2277](https://github.com/labstack/echo/pull/2277)
|
||||||
|
|
||||||
|
|
||||||
|
## v4.10.1 - 2023-02-19
|
||||||
|
|
||||||
|
**Security**
|
||||||
|
|
||||||
|
* Upgrade deps due to the latest golang.org/x/net vulnerability [#2402](https://github.com/labstack/echo/pull/2402)
|
||||||
|
|
||||||
|
|
||||||
|
**Enhancements**
|
||||||
|
|
||||||
|
* Add new JWT repository to the README [#2377](https://github.com/labstack/echo/pull/2377)
|
||||||
|
* Return an empty string for ctx.path if there is no registered path [#2385](https://github.com/labstack/echo/pull/2385)
|
||||||
|
* Add context timeout middleware [#2380](https://github.com/labstack/echo/pull/2380)
|
||||||
|
* Update link to jaegertracing [#2394](https://github.com/labstack/echo/pull/2394)
|
||||||
|
|
||||||
|
|
||||||
## v4.10.0 - 2022-12-27
|
## v4.10.0 - 2022-12-27
|
||||||
|
|
||||||
**Security**
|
**Security**
|
||||||
|
4
vendor/github.com/labstack/echo/v4/Makefile
generated
vendored
4
vendor/github.com/labstack/echo/v4/Makefile
generated
vendored
@ -31,6 +31,6 @@ benchmark: ## Run benchmarks
|
|||||||
help: ## Display this help screen
|
help: ## Display this help screen
|
||||||
@grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
@grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
||||||
|
|
||||||
goversion ?= "1.17"
|
goversion ?= "1.19"
|
||||||
test_version: ## Run tests inside Docker with given version (defaults to 1.17 oldest supported). Example: make test_version goversion=1.17
|
test_version: ## Run tests inside Docker with given version (defaults to 1.19 oldest supported). Example: make test_version goversion=1.19
|
||||||
@docker run --rm -it -v $(shell pwd):/project golang:$(goversion) /bin/sh -c "cd /project && make init check"
|
@docker run --rm -it -v $(shell pwd):/project golang:$(goversion) /bin/sh -c "cd /project && make init check"
|
||||||
|
71
vendor/github.com/labstack/echo/v4/README.md
generated
vendored
71
vendor/github.com/labstack/echo/v4/README.md
generated
vendored
@ -3,26 +3,24 @@
|
|||||||
[![Sourcegraph](https://sourcegraph.com/github.com/labstack/echo/-/badge.svg?style=flat-square)](https://sourcegraph.com/github.com/labstack/echo?badge)
|
[![Sourcegraph](https://sourcegraph.com/github.com/labstack/echo/-/badge.svg?style=flat-square)](https://sourcegraph.com/github.com/labstack/echo?badge)
|
||||||
[![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](https://pkg.go.dev/github.com/labstack/echo/v4)
|
[![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](https://pkg.go.dev/github.com/labstack/echo/v4)
|
||||||
[![Go Report Card](https://goreportcard.com/badge/github.com/labstack/echo?style=flat-square)](https://goreportcard.com/report/github.com/labstack/echo)
|
[![Go Report Card](https://goreportcard.com/badge/github.com/labstack/echo?style=flat-square)](https://goreportcard.com/report/github.com/labstack/echo)
|
||||||
[![Build Status](http://img.shields.io/travis/labstack/echo.svg?style=flat-square)](https://travis-ci.org/labstack/echo)
|
[![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/labstack/echo/echo.yml?style=flat-square)](https://github.com/labstack/echo/actions)
|
||||||
[![Codecov](https://img.shields.io/codecov/c/github/labstack/echo.svg?style=flat-square)](https://codecov.io/gh/labstack/echo)
|
[![Codecov](https://img.shields.io/codecov/c/github/labstack/echo.svg?style=flat-square)](https://codecov.io/gh/labstack/echo)
|
||||||
[![Forum](https://img.shields.io/badge/community-forum-00afd1.svg?style=flat-square)](https://github.com/labstack/echo/discussions)
|
[![Forum](https://img.shields.io/badge/community-forum-00afd1.svg?style=flat-square)](https://github.com/labstack/echo/discussions)
|
||||||
[![Twitter](https://img.shields.io/badge/twitter-@labstack-55acee.svg?style=flat-square)](https://twitter.com/labstack)
|
[![Twitter](https://img.shields.io/badge/twitter-@labstack-55acee.svg?style=flat-square)](https://twitter.com/labstack)
|
||||||
[![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://raw.githubusercontent.com/labstack/echo/master/LICENSE)
|
[![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://raw.githubusercontent.com/labstack/echo/master/LICENSE)
|
||||||
|
|
||||||
## Supported Go versions
|
## Echo
|
||||||
|
|
||||||
Latest version of Echo supports last four Go major [releases](https://go.dev/doc/devel/release) and might work with older versions.
|
High performance, extensible, minimalist Go web framework.
|
||||||
|
|
||||||
As of version 4.0.0, Echo is available as a [Go module](https://github.com/golang/go/wiki/Modules).
|
* [Official website](https://echo.labstack.com)
|
||||||
Therefore a Go version capable of understanding /vN suffixed imports is required:
|
* [Quick start](https://echo.labstack.com/docs/quick-start)
|
||||||
|
* [Middlewares](https://echo.labstack.com/docs/category/middleware)
|
||||||
|
|
||||||
|
Help and questions: [Github Discussions](https://github.com/labstack/echo/discussions)
|
||||||
|
|
||||||
|
|
||||||
Any of these versions will allow you to import Echo as `github.com/labstack/echo/v4` which is the recommended
|
### Feature Overview
|
||||||
way of using Echo going forward.
|
|
||||||
|
|
||||||
For older versions, please use the latest v3 tag.
|
|
||||||
|
|
||||||
## Feature Overview
|
|
||||||
|
|
||||||
- Optimized HTTP router which smartly prioritize routes
|
- Optimized HTTP router which smartly prioritize routes
|
||||||
- Build robust and scalable RESTful APIs
|
- Build robust and scalable RESTful APIs
|
||||||
@ -38,6 +36,18 @@ For older versions, please use the latest v3 tag.
|
|||||||
- Automatic TLS via Let’s Encrypt
|
- Automatic TLS via Let’s Encrypt
|
||||||
- HTTP/2 support
|
- HTTP/2 support
|
||||||
|
|
||||||
|
## Sponsors
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<a href="https://encore.dev" style="display: inline-flex; align-items: center; gap: 10px">
|
||||||
|
<img src="https://user-images.githubusercontent.com/78424526/214602214-52e0483a-b5fc-4d4c-b03e-0b7b23e012df.svg" height="28px" alt="encore icon"></img>
|
||||||
|
<b>Encore – the platform for building Go-based cloud backends</b>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
Click [here](https://github.com/sponsors/labstack) for more information on sponsorship.
|
||||||
|
|
||||||
## Benchmarks
|
## Benchmarks
|
||||||
|
|
||||||
Date: 2020/11/11<br>
|
Date: 2020/11/11<br>
|
||||||
@ -57,6 +67,7 @@ The benchmarks above were run on an Intel(R) Core(TM) i7-6820HQ CPU @ 2.70GHz
|
|||||||
// go get github.com/labstack/echo/{version}
|
// go get github.com/labstack/echo/{version}
|
||||||
go get github.com/labstack/echo/v4
|
go get github.com/labstack/echo/v4
|
||||||
```
|
```
|
||||||
|
Latest version of Echo supports last four Go major [releases](https://go.dev/doc/devel/release) and might work with older versions.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
@ -90,25 +101,33 @@ func hello(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
# Third-party middlewares
|
# Official middleware repositories
|
||||||
|
|
||||||
| Repository | Description |
|
Following list of middleware is maintained by Echo team.
|
||||||
|------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
||||||
| [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 |
|
| Repository | Description |
|
||||||
| [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/labstack/echo-jwt](https://github.com/labstack/echo-jwt) | [JWT](https://github.com/golang-jwt/jwt) middleware |
|
||||||
| [github.com/ziflex/lecho](https://github.com/ziflex/lecho) | [Zerolog](https://github.com/rs/zerolog) logging library wrapper for Echo logger interface. |
|
| [github.com/labstack/echo-contrib](https://github.com/labstack/echo-contrib) | [casbin](https://github.com/casbin/casbin), [gorilla/sessions](https://github.com/gorilla/sessions), [jaegertracing](https://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 |
|
||||||
| [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. |
|
# Third-party middleware repositories
|
||||||
| [github.com/mikestefanello/pagoda](https://github.com/mikestefanello/pagoda) | Rapid, easy full-stack web development starter kit built with Echo. |
|
|
||||||
| [github.com/go-woo/protoc-gen-echo](https://github.com/go-woo/protoc-gen-echo) | ProtoBuf generate Echo server side code |
|
Be careful when adding 3rd party middleware. Echo teams does not have time or manpower to guarantee safety and quality
|
||||||
|
of middlewares in this list.
|
||||||
|
|
||||||
|
| Repository | Description |
|
||||||
|
|------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| [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/samber/slog-echo](https://github.com/samber/slog-echo) | Go [slog](https://pkg.go.dev/golang.org/x/exp/slog) 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. |
|
||||||
|
| [github.com/go-woo/protoc-gen-echo](https://github.com/go-woo/protoc-gen-echo) | ProtoBuf generate Echo server side code |
|
||||||
|
|
||||||
Please send a PR to add your own library here.
|
Please send a PR to add your own library here.
|
||||||
|
|
||||||
## Help
|
|
||||||
|
|
||||||
- [Forum](https://github.com/labstack/echo/discussions)
|
|
||||||
|
|
||||||
## Contribute
|
## Contribute
|
||||||
|
|
||||||
**Use issues for everything**
|
**Use issues for everything**
|
||||||
|
150
vendor/github.com/labstack/echo/v4/bind.go
generated
vendored
150
vendor/github.com/labstack/echo/v4/bind.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package echo
|
package echo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -11,23 +14,28 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// Binder is the interface that wraps the Bind method.
|
||||||
// Binder is the interface that wraps the Bind method.
|
type Binder interface {
|
||||||
Binder interface {
|
Bind(i interface{}, c Context) error
|
||||||
Bind(i interface{}, c Context) error
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultBinder is the default implementation of the Binder interface.
|
// DefaultBinder is the default implementation of the Binder interface.
|
||||||
DefaultBinder struct{}
|
type DefaultBinder struct{}
|
||||||
|
|
||||||
// BindUnmarshaler is the interface used to wrap the UnmarshalParam method.
|
// BindUnmarshaler is the interface used to wrap the UnmarshalParam method.
|
||||||
// Types that don't implement this, but do implement encoding.TextUnmarshaler
|
// Types that don't implement this, but do implement encoding.TextUnmarshaler
|
||||||
// will use that interface instead.
|
// will use that interface instead.
|
||||||
BindUnmarshaler interface {
|
type BindUnmarshaler interface {
|
||||||
// UnmarshalParam decodes and assigns a value from an form or query param.
|
// UnmarshalParam decodes and assigns a value from an form or query param.
|
||||||
UnmarshalParam(param string) error
|
UnmarshalParam(param string) error
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
// bindMultipleUnmarshaler is used by binder to unmarshal multiple values from request at once to
|
||||||
|
// type implementing this interface. For example request could have multiple query fields `?a=1&a=2&b=test` in that case
|
||||||
|
// for `a` following slice `["1", "2"] will be passed to unmarshaller.
|
||||||
|
type bindMultipleUnmarshaler interface {
|
||||||
|
UnmarshalParams(params []string) error
|
||||||
|
}
|
||||||
|
|
||||||
// BindPathParams binds path params to bindable object
|
// BindPathParams binds path params to bindable object
|
||||||
func (b *DefaultBinder) BindPathParams(c Context, i interface{}) error {
|
func (b *DefaultBinder) BindPathParams(c Context, i interface{}) error {
|
||||||
@ -114,7 +122,7 @@ func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) {
|
|||||||
// Only bind query parameters for GET/DELETE/HEAD to avoid unexpected behavior with destination struct binding from body.
|
// Only bind query parameters for GET/DELETE/HEAD to avoid unexpected behavior with destination struct binding from body.
|
||||||
// For example a request URL `&id=1&lang=en` with body `{"id":100,"lang":"de"}` would lead to precedence issues.
|
// For example a request URL `&id=1&lang=en` with body `{"id":100,"lang":"de"}` would lead to precedence issues.
|
||||||
// The HTTP method check restores pre-v4.1.11 behavior to avoid these problems (see issue #1670)
|
// The HTTP method check restores pre-v4.1.11 behavior to avoid these problems (see issue #1670)
|
||||||
method := c.Request().Method
|
method := c.Request().Method
|
||||||
if method == http.MethodGet || method == http.MethodDelete || method == http.MethodHead {
|
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
|
||||||
@ -131,10 +139,29 @@ func (b *DefaultBinder) bindData(destination interface{}, data map[string][]stri
|
|||||||
typ := reflect.TypeOf(destination).Elem()
|
typ := reflect.TypeOf(destination).Elem()
|
||||||
val := reflect.ValueOf(destination).Elem()
|
val := reflect.ValueOf(destination).Elem()
|
||||||
|
|
||||||
// Map
|
// Support binding to limited Map destinations:
|
||||||
if typ.Kind() == reflect.Map {
|
// - map[string][]string,
|
||||||
|
// - map[string]string <-- (binds first value from data slice)
|
||||||
|
// - map[string]interface{}
|
||||||
|
// You are better off binding to struct but there are user who want this map feature. Source of data for these cases are:
|
||||||
|
// params,query,header,form as these sources produce string values, most of the time slice of strings, actually.
|
||||||
|
if typ.Kind() == reflect.Map && typ.Key().Kind() == reflect.String {
|
||||||
|
k := typ.Elem().Kind()
|
||||||
|
isElemInterface := k == reflect.Interface
|
||||||
|
isElemString := k == reflect.String
|
||||||
|
isElemSliceOfStrings := k == reflect.Slice && typ.Elem().Elem().Kind() == reflect.String
|
||||||
|
if !(isElemSliceOfStrings || isElemString || isElemInterface) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if val.IsNil() {
|
||||||
|
val.Set(reflect.MakeMap(typ))
|
||||||
|
}
|
||||||
for k, v := range data {
|
for k, v := range data {
|
||||||
val.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v[0]))
|
if isElemString {
|
||||||
|
val.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v[0]))
|
||||||
|
} else {
|
||||||
|
val.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -161,14 +188,14 @@ func (b *DefaultBinder) bindData(destination interface{}, data map[string][]stri
|
|||||||
}
|
}
|
||||||
structFieldKind := structField.Kind()
|
structFieldKind := structField.Kind()
|
||||||
inputFieldName := typeField.Tag.Get(tag)
|
inputFieldName := typeField.Tag.Get(tag)
|
||||||
if typeField.Anonymous && structField.Kind() == reflect.Struct && inputFieldName != "" {
|
if typeField.Anonymous && structFieldKind == reflect.Struct && inputFieldName != "" {
|
||||||
// if anonymous struct with query/param/form tags, report an error
|
// if anonymous struct with query/param/form tags, report an error
|
||||||
return errors.New("query/param/form tags are not allowed with anonymous struct field")
|
return errors.New("query/param/form tags are not allowed with anonymous struct field")
|
||||||
}
|
}
|
||||||
|
|
||||||
if inputFieldName == "" {
|
if inputFieldName == "" {
|
||||||
// If tag is nil, we inspect if the field is a not BindUnmarshaler struct and try to bind data into it (might contains fields with tags).
|
// If tag is nil, we inspect if the field is a not BindUnmarshaler struct and try to bind data into it (might contains fields with tags).
|
||||||
// structs that implement BindUnmarshaler are binded only when they have explicit tag
|
// structs that implement BindUnmarshaler are bound only when they have explicit tag
|
||||||
if _, ok := structField.Addr().Interface().(BindUnmarshaler); !ok && structFieldKind == reflect.Struct {
|
if _, ok := structField.Addr().Interface().(BindUnmarshaler); !ok && structFieldKind == reflect.Struct {
|
||||||
if err := b.bindData(structField.Addr().Interface(), data, tag); err != nil {
|
if err := b.bindData(structField.Addr().Interface(), data, tag); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -197,27 +224,46 @@ func (b *DefaultBinder) bindData(destination interface{}, data map[string][]stri
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call this first, in case we're dealing with an alias to an array type
|
// NOTE: algorithm here is not particularly sophisticated. It probably does not work with absurd types like `**[]*int`
|
||||||
if ok, err := unmarshalField(typeField.Type.Kind(), inputValue[0], structField); ok {
|
// but it is smart enough to handle niche cases like `*int`,`*[]string`,`[]*int` .
|
||||||
|
|
||||||
|
// try unmarshalling first, in case we're dealing with an alias to an array type
|
||||||
|
if ok, err := unmarshalInputsToField(typeField.Type.Kind(), inputValue, structField); ok {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
numElems := len(inputValue)
|
if ok, err := unmarshalInputToField(typeField.Type.Kind(), inputValue[0], structField); ok {
|
||||||
if structFieldKind == reflect.Slice && numElems > 0 {
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// we could be dealing with pointer to slice `*[]string` so dereference it. There are wierd OpenAPI generators
|
||||||
|
// that could create struct fields like that.
|
||||||
|
if structFieldKind == reflect.Pointer {
|
||||||
|
structFieldKind = structField.Elem().Kind()
|
||||||
|
structField = structField.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if structFieldKind == reflect.Slice {
|
||||||
sliceOf := structField.Type().Elem().Kind()
|
sliceOf := structField.Type().Elem().Kind()
|
||||||
|
numElems := len(inputValue)
|
||||||
slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
|
slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
|
||||||
for j := 0; j < numElems; j++ {
|
for j := 0; j < numElems; j++ {
|
||||||
if err := setWithProperType(sliceOf, inputValue[j], slice.Index(j)); err != nil {
|
if err := setWithProperType(sliceOf, inputValue[j], slice.Index(j)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val.Field(i).Set(slice)
|
structField.Set(slice)
|
||||||
} else if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
|
continue
|
||||||
return err
|
}
|
||||||
|
|
||||||
|
if err := setWithProperType(structFieldKind, inputValue[0], structField); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -225,7 +271,7 @@ func (b *DefaultBinder) bindData(destination interface{}, data map[string][]stri
|
|||||||
|
|
||||||
func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value) error {
|
func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value) error {
|
||||||
// But also call it here, in case we're dealing with an array of BindUnmarshalers
|
// But also call it here, in case we're dealing with an array of BindUnmarshalers
|
||||||
if ok, err := unmarshalField(valueKind, val, structField); ok {
|
if ok, err := unmarshalInputToField(valueKind, val, structField); ok {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,35 +312,41 @@ func setWithProperType(valueKind reflect.Kind, val string, structField reflect.V
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmarshalField(valueKind reflect.Kind, val string, field reflect.Value) (bool, error) {
|
func unmarshalInputsToField(valueKind reflect.Kind, values []string, field reflect.Value) (bool, error) {
|
||||||
switch valueKind {
|
if valueKind == reflect.Ptr {
|
||||||
case reflect.Ptr:
|
if field.IsNil() {
|
||||||
return unmarshalFieldPtr(val, field)
|
field.Set(reflect.New(field.Type().Elem()))
|
||||||
default:
|
}
|
||||||
return unmarshalFieldNonPtr(val, field)
|
field = field.Elem()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fieldIValue := field.Addr().Interface()
|
||||||
|
unmarshaler, ok := fieldIValue.(bindMultipleUnmarshaler)
|
||||||
|
if !ok {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return true, unmarshaler.UnmarshalParams(values)
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmarshalFieldNonPtr(value string, field reflect.Value) (bool, error) {
|
func unmarshalInputToField(valueKind reflect.Kind, val string, field reflect.Value) (bool, error) {
|
||||||
fieldIValue := field.Addr().Interface()
|
if valueKind == reflect.Ptr {
|
||||||
if unmarshaler, ok := fieldIValue.(BindUnmarshaler); ok {
|
if field.IsNil() {
|
||||||
return true, unmarshaler.UnmarshalParam(value)
|
field.Set(reflect.New(field.Type().Elem()))
|
||||||
|
}
|
||||||
|
field = field.Elem()
|
||||||
}
|
}
|
||||||
if unmarshaler, ok := fieldIValue.(encoding.TextUnmarshaler); ok {
|
|
||||||
return true, unmarshaler.UnmarshalText([]byte(value))
|
fieldIValue := field.Addr().Interface()
|
||||||
|
switch unmarshaler := fieldIValue.(type) {
|
||||||
|
case BindUnmarshaler:
|
||||||
|
return true, unmarshaler.UnmarshalParam(val)
|
||||||
|
case encoding.TextUnmarshaler:
|
||||||
|
return true, unmarshaler.UnmarshalText([]byte(val))
|
||||||
}
|
}
|
||||||
|
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmarshalFieldPtr(value string, field reflect.Value) (bool, error) {
|
|
||||||
if field.IsNil() {
|
|
||||||
// Initialize the pointer to a nil value
|
|
||||||
field.Set(reflect.New(field.Type().Elem()))
|
|
||||||
}
|
|
||||||
return unmarshalFieldNonPtr(value, field.Elem())
|
|
||||||
}
|
|
||||||
|
|
||||||
func setIntField(value string, bitSize int, field reflect.Value) error {
|
func setIntField(value string, bitSize int, field reflect.Value) error {
|
||||||
if value == "" {
|
if value == "" {
|
||||||
value = "0"
|
value = "0"
|
||||||
|
21
vendor/github.com/labstack/echo/v4/binder.go
generated
vendored
21
vendor/github.com/labstack/echo/v4/binder.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package echo
|
package echo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -1236,7 +1239,7 @@ func (b *ValueBinder) durations(sourceParam string, values []string, dest *[]tim
|
|||||||
// Example: 1609180603 bind to 2020-12-28T18:36:43.000000000+00:00
|
// Example: 1609180603 bind to 2020-12-28T18:36:43.000000000+00:00
|
||||||
//
|
//
|
||||||
// Note:
|
// Note:
|
||||||
// * time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
|
// - time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
|
||||||
func (b *ValueBinder) UnixTime(sourceParam string, dest *time.Time) *ValueBinder {
|
func (b *ValueBinder) UnixTime(sourceParam string, dest *time.Time) *ValueBinder {
|
||||||
return b.unixTime(sourceParam, dest, false, time.Second)
|
return b.unixTime(sourceParam, dest, false, time.Second)
|
||||||
}
|
}
|
||||||
@ -1247,7 +1250,7 @@ func (b *ValueBinder) UnixTime(sourceParam string, dest *time.Time) *ValueBinder
|
|||||||
// Example: 1609180603 bind to 2020-12-28T18:36:43.000000000+00:00
|
// Example: 1609180603 bind to 2020-12-28T18:36:43.000000000+00:00
|
||||||
//
|
//
|
||||||
// Note:
|
// Note:
|
||||||
// * time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
|
// - time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
|
||||||
func (b *ValueBinder) MustUnixTime(sourceParam string, dest *time.Time) *ValueBinder {
|
func (b *ValueBinder) MustUnixTime(sourceParam string, dest *time.Time) *ValueBinder {
|
||||||
return b.unixTime(sourceParam, dest, true, time.Second)
|
return b.unixTime(sourceParam, dest, true, time.Second)
|
||||||
}
|
}
|
||||||
@ -1257,7 +1260,7 @@ func (b *ValueBinder) MustUnixTime(sourceParam string, dest *time.Time) *ValueBi
|
|||||||
// Example: 1647184410140 bind to 2022-03-13T15:13:30.140000000+00:00
|
// Example: 1647184410140 bind to 2022-03-13T15:13:30.140000000+00:00
|
||||||
//
|
//
|
||||||
// Note:
|
// Note:
|
||||||
// * time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
|
// - time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
|
||||||
func (b *ValueBinder) UnixTimeMilli(sourceParam string, dest *time.Time) *ValueBinder {
|
func (b *ValueBinder) UnixTimeMilli(sourceParam string, dest *time.Time) *ValueBinder {
|
||||||
return b.unixTime(sourceParam, dest, false, time.Millisecond)
|
return b.unixTime(sourceParam, dest, false, time.Millisecond)
|
||||||
}
|
}
|
||||||
@ -1268,7 +1271,7 @@ func (b *ValueBinder) UnixTimeMilli(sourceParam string, dest *time.Time) *ValueB
|
|||||||
// Example: 1647184410140 bind to 2022-03-13T15:13:30.140000000+00:00
|
// Example: 1647184410140 bind to 2022-03-13T15:13:30.140000000+00:00
|
||||||
//
|
//
|
||||||
// Note:
|
// Note:
|
||||||
// * time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
|
// - time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
|
||||||
func (b *ValueBinder) MustUnixTimeMilli(sourceParam string, dest *time.Time) *ValueBinder {
|
func (b *ValueBinder) MustUnixTimeMilli(sourceParam string, dest *time.Time) *ValueBinder {
|
||||||
return b.unixTime(sourceParam, dest, true, time.Millisecond)
|
return b.unixTime(sourceParam, dest, true, time.Millisecond)
|
||||||
}
|
}
|
||||||
@ -1280,8 +1283,8 @@ func (b *ValueBinder) MustUnixTimeMilli(sourceParam string, dest *time.Time) *Va
|
|||||||
// Example: 999999999 binds to 1970-01-01T00:00:00.999999999+00:00
|
// Example: 999999999 binds to 1970-01-01T00:00:00.999999999+00:00
|
||||||
//
|
//
|
||||||
// Note:
|
// Note:
|
||||||
// * time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
|
// - time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
|
||||||
// * Javascript's Number type only has about 53 bits of precision (Number.MAX_SAFE_INTEGER = 9007199254740991). Compare it to 1609180603123456789 in example.
|
// - Javascript's Number type only has about 53 bits of precision (Number.MAX_SAFE_INTEGER = 9007199254740991). Compare it to 1609180603123456789 in example.
|
||||||
func (b *ValueBinder) UnixTimeNano(sourceParam string, dest *time.Time) *ValueBinder {
|
func (b *ValueBinder) UnixTimeNano(sourceParam string, dest *time.Time) *ValueBinder {
|
||||||
return b.unixTime(sourceParam, dest, false, time.Nanosecond)
|
return b.unixTime(sourceParam, dest, false, time.Nanosecond)
|
||||||
}
|
}
|
||||||
@ -1294,8 +1297,8 @@ func (b *ValueBinder) UnixTimeNano(sourceParam string, dest *time.Time) *ValueBi
|
|||||||
// Example: 999999999 binds to 1970-01-01T00:00:00.999999999+00:00
|
// Example: 999999999 binds to 1970-01-01T00:00:00.999999999+00:00
|
||||||
//
|
//
|
||||||
// Note:
|
// Note:
|
||||||
// * time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
|
// - time.Time{} (param is empty) and time.Unix(0,0) (param = "0") are not equal
|
||||||
// * Javascript's Number type only has about 53 bits of precision (Number.MAX_SAFE_INTEGER = 9007199254740991). Compare it to 1609180603123456789 in example.
|
// - Javascript's Number type only has about 53 bits of precision (Number.MAX_SAFE_INTEGER = 9007199254740991). Compare it to 1609180603123456789 in example.
|
||||||
func (b *ValueBinder) MustUnixTimeNano(sourceParam string, dest *time.Time) *ValueBinder {
|
func (b *ValueBinder) MustUnixTimeNano(sourceParam string, dest *time.Time) *ValueBinder {
|
||||||
return b.unixTime(sourceParam, dest, true, time.Nanosecond)
|
return b.unixTime(sourceParam, dest, true, time.Nanosecond)
|
||||||
}
|
}
|
||||||
@ -1323,7 +1326,7 @@ func (b *ValueBinder) unixTime(sourceParam string, dest *time.Time, valueMustExi
|
|||||||
case time.Second:
|
case time.Second:
|
||||||
*dest = time.Unix(n, 0)
|
*dest = time.Unix(n, 0)
|
||||||
case time.Millisecond:
|
case time.Millisecond:
|
||||||
*dest = time.Unix(n/1e3, (n%1e3)*1e6) // TODO: time.UnixMilli(n) exists since Go1.17 switch to that when min version allows
|
*dest = time.UnixMilli(n)
|
||||||
case time.Nanosecond:
|
case time.Nanosecond:
|
||||||
*dest = time.Unix(0, n)
|
*dest = time.Unix(0, n)
|
||||||
}
|
}
|
||||||
|
325
vendor/github.com/labstack/echo/v4/context.go
generated
vendored
325
vendor/github.com/labstack/echo/v4/context.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package echo
|
package echo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -13,204 +16,216 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// Context represents the context of the current HTTP request. It holds request and
|
||||||
// Context represents the context of the current HTTP request. It holds request and
|
// response objects, path, path parameters, data and registered handler.
|
||||||
// response objects, path, path parameters, data and registered handler.
|
type Context interface {
|
||||||
Context interface {
|
// Request returns `*http.Request`.
|
||||||
// Request returns `*http.Request`.
|
Request() *http.Request
|
||||||
Request() *http.Request
|
|
||||||
|
|
||||||
// SetRequest sets `*http.Request`.
|
// SetRequest sets `*http.Request`.
|
||||||
SetRequest(r *http.Request)
|
SetRequest(r *http.Request)
|
||||||
|
|
||||||
// SetResponse sets `*Response`.
|
// SetResponse sets `*Response`.
|
||||||
SetResponse(r *Response)
|
SetResponse(r *Response)
|
||||||
|
|
||||||
// Response returns `*Response`.
|
// Response returns `*Response`.
|
||||||
Response() *Response
|
Response() *Response
|
||||||
|
|
||||||
// IsTLS returns true if HTTP connection is TLS otherwise false.
|
// IsTLS returns true if HTTP connection is TLS otherwise false.
|
||||||
IsTLS() bool
|
IsTLS() bool
|
||||||
|
|
||||||
// IsWebSocket returns true if HTTP connection is WebSocket otherwise false.
|
// IsWebSocket returns true if HTTP connection is WebSocket otherwise false.
|
||||||
IsWebSocket() bool
|
IsWebSocket() bool
|
||||||
|
|
||||||
// Scheme returns the HTTP protocol scheme, `http` or `https`.
|
// Scheme returns the HTTP protocol scheme, `http` or `https`.
|
||||||
Scheme() string
|
Scheme() string
|
||||||
|
|
||||||
// RealIP returns the client's network address based on `X-Forwarded-For`
|
// RealIP returns the client's network address based on `X-Forwarded-For`
|
||||||
// or `X-Real-IP` request header.
|
// or `X-Real-IP` request header.
|
||||||
// The behavior can be configured using `Echo#IPExtractor`.
|
// The behavior can be configured using `Echo#IPExtractor`.
|
||||||
RealIP() string
|
RealIP() string
|
||||||
|
|
||||||
// Path returns the registered path for the handler.
|
// Path returns the registered path for the handler.
|
||||||
Path() string
|
Path() string
|
||||||
|
|
||||||
// SetPath sets the registered path for the handler.
|
// SetPath sets the registered path for the handler.
|
||||||
SetPath(p string)
|
SetPath(p string)
|
||||||
|
|
||||||
// Param returns path parameter by name.
|
// Param returns path parameter by name.
|
||||||
Param(name string) string
|
Param(name string) string
|
||||||
|
|
||||||
// ParamNames returns path parameter names.
|
// ParamNames returns path parameter names.
|
||||||
ParamNames() []string
|
ParamNames() []string
|
||||||
|
|
||||||
// SetParamNames sets path parameter names.
|
// SetParamNames sets path parameter names.
|
||||||
SetParamNames(names ...string)
|
SetParamNames(names ...string)
|
||||||
|
|
||||||
// ParamValues returns path parameter values.
|
// ParamValues returns path parameter values.
|
||||||
ParamValues() []string
|
ParamValues() []string
|
||||||
|
|
||||||
// SetParamValues sets path parameter values.
|
// SetParamValues sets path parameter values.
|
||||||
SetParamValues(values ...string)
|
SetParamValues(values ...string)
|
||||||
|
|
||||||
// QueryParam returns the query param for the provided name.
|
// QueryParam returns the query param for the provided name.
|
||||||
QueryParam(name string) string
|
QueryParam(name string) string
|
||||||
|
|
||||||
// QueryParams returns the query parameters as `url.Values`.
|
// QueryParams returns the query parameters as `url.Values`.
|
||||||
QueryParams() url.Values
|
QueryParams() url.Values
|
||||||
|
|
||||||
// QueryString returns the URL query string.
|
// QueryString returns the URL query string.
|
||||||
QueryString() string
|
QueryString() string
|
||||||
|
|
||||||
// FormValue returns the form field value for the provided name.
|
// FormValue returns the form field value for the provided name.
|
||||||
FormValue(name string) string
|
FormValue(name string) string
|
||||||
|
|
||||||
// FormParams returns the form parameters as `url.Values`.
|
// FormParams returns the form parameters as `url.Values`.
|
||||||
FormParams() (url.Values, error)
|
FormParams() (url.Values, error)
|
||||||
|
|
||||||
// FormFile returns the multipart form file for the provided name.
|
// FormFile returns the multipart form file for the provided name.
|
||||||
FormFile(name string) (*multipart.FileHeader, error)
|
FormFile(name string) (*multipart.FileHeader, error)
|
||||||
|
|
||||||
// MultipartForm returns the multipart form.
|
// MultipartForm returns the multipart form.
|
||||||
MultipartForm() (*multipart.Form, error)
|
MultipartForm() (*multipart.Form, error)
|
||||||
|
|
||||||
// Cookie returns the named cookie provided in the request.
|
// Cookie returns the named cookie provided in the request.
|
||||||
Cookie(name string) (*http.Cookie, error)
|
Cookie(name string) (*http.Cookie, error)
|
||||||
|
|
||||||
// SetCookie adds a `Set-Cookie` header in HTTP response.
|
// SetCookie adds a `Set-Cookie` header in HTTP response.
|
||||||
SetCookie(cookie *http.Cookie)
|
SetCookie(cookie *http.Cookie)
|
||||||
|
|
||||||
// Cookies returns the HTTP cookies sent with the request.
|
// Cookies returns the HTTP cookies sent with the request.
|
||||||
Cookies() []*http.Cookie
|
Cookies() []*http.Cookie
|
||||||
|
|
||||||
// Get retrieves data from the context.
|
// Get retrieves data from the context.
|
||||||
Get(key string) interface{}
|
Get(key string) interface{}
|
||||||
|
|
||||||
// Set saves data in the context.
|
// Set saves data in the context.
|
||||||
Set(key string, val interface{})
|
Set(key string, val interface{})
|
||||||
|
|
||||||
// Bind binds the request body into provided type `i`. The default binder
|
// Bind binds path params, query params and the request body into provided type `i`. The default binder
|
||||||
// does it based on Content-Type header.
|
// binds body based on Content-Type header.
|
||||||
Bind(i interface{}) error
|
Bind(i interface{}) error
|
||||||
|
|
||||||
// Validate validates provided `i`. It is usually called after `Context#Bind()`.
|
// Validate validates provided `i`. It is usually called after `Context#Bind()`.
|
||||||
// Validator must be registered using `Echo#Validator`.
|
// Validator must be registered using `Echo#Validator`.
|
||||||
Validate(i interface{}) error
|
Validate(i interface{}) error
|
||||||
|
|
||||||
// Render renders a template with data and sends a text/html response with status
|
// Render renders a template with data and sends a text/html response with status
|
||||||
// code. Renderer must be registered using `Echo.Renderer`.
|
// code. Renderer must be registered using `Echo.Renderer`.
|
||||||
Render(code int, name string, data interface{}) error
|
Render(code int, name string, data interface{}) error
|
||||||
|
|
||||||
// HTML sends an HTTP response with status code.
|
// HTML sends an HTTP response with status code.
|
||||||
HTML(code int, html string) error
|
HTML(code int, html string) error
|
||||||
|
|
||||||
// HTMLBlob sends an HTTP blob response with status code.
|
// HTMLBlob sends an HTTP blob response with status code.
|
||||||
HTMLBlob(code int, b []byte) error
|
HTMLBlob(code int, b []byte) error
|
||||||
|
|
||||||
// String sends a string response with status code.
|
// String sends a string response with status code.
|
||||||
String(code int, s string) error
|
String(code int, s string) error
|
||||||
|
|
||||||
// JSON sends a JSON response with status code.
|
// JSON sends a JSON response with status code.
|
||||||
JSON(code int, i interface{}) error
|
JSON(code int, i interface{}) error
|
||||||
|
|
||||||
// JSONPretty sends a pretty-print JSON with status code.
|
// JSONPretty sends a pretty-print JSON with status code.
|
||||||
JSONPretty(code int, i interface{}, indent string) error
|
JSONPretty(code int, i interface{}, indent string) error
|
||||||
|
|
||||||
// JSONBlob sends a JSON blob response with status code.
|
// JSONBlob sends a JSON blob response with status code.
|
||||||
JSONBlob(code int, b []byte) error
|
JSONBlob(code int, b []byte) error
|
||||||
|
|
||||||
// JSONP sends a JSONP response with status code. It uses `callback` to construct
|
// JSONP sends a JSONP response with status code. It uses `callback` to construct
|
||||||
// the JSONP payload.
|
// the JSONP payload.
|
||||||
JSONP(code int, callback string, i interface{}) error
|
JSONP(code int, callback string, i interface{}) error
|
||||||
|
|
||||||
// JSONPBlob sends a JSONP blob response with status code. It uses `callback`
|
// JSONPBlob sends a JSONP blob response with status code. It uses `callback`
|
||||||
// to construct the JSONP payload.
|
// to construct the JSONP payload.
|
||||||
JSONPBlob(code int, callback string, b []byte) error
|
JSONPBlob(code int, callback string, b []byte) error
|
||||||
|
|
||||||
// XML sends an XML response with status code.
|
// XML sends an XML response with status code.
|
||||||
XML(code int, i interface{}) error
|
XML(code int, i interface{}) error
|
||||||
|
|
||||||
// XMLPretty sends a pretty-print XML with status code.
|
// XMLPretty sends a pretty-print XML with status code.
|
||||||
XMLPretty(code int, i interface{}, indent string) error
|
XMLPretty(code int, i interface{}, indent string) error
|
||||||
|
|
||||||
// XMLBlob sends an XML blob response with status code.
|
// XMLBlob sends an XML blob response with status code.
|
||||||
XMLBlob(code int, b []byte) error
|
XMLBlob(code int, b []byte) error
|
||||||
|
|
||||||
// Blob sends a blob response with status code and content type.
|
// Blob sends a blob response with status code and content type.
|
||||||
Blob(code int, contentType string, b []byte) error
|
Blob(code int, contentType string, b []byte) error
|
||||||
|
|
||||||
// Stream sends a streaming response with status code and content type.
|
// Stream sends a streaming response with status code and content type.
|
||||||
Stream(code int, contentType string, r io.Reader) error
|
Stream(code int, contentType string, r io.Reader) error
|
||||||
|
|
||||||
// File sends a response with the content of the file.
|
// File sends a response with the content of the file.
|
||||||
File(file string) error
|
File(file string) error
|
||||||
|
|
||||||
// Attachment sends a response as attachment, prompting client to save the
|
// Attachment sends a response as attachment, prompting client to save the
|
||||||
// file.
|
// file.
|
||||||
Attachment(file string, name string) error
|
Attachment(file string, name string) error
|
||||||
|
|
||||||
// Inline sends a response as inline, opening the file in the browser.
|
// Inline sends a response as inline, opening the file in the browser.
|
||||||
Inline(file string, name string) error
|
Inline(file string, name string) error
|
||||||
|
|
||||||
// NoContent sends a response with no body and a status code.
|
// NoContent sends a response with no body and a status code.
|
||||||
NoContent(code int) error
|
NoContent(code int) error
|
||||||
|
|
||||||
// Redirect redirects the request to a provided URL with status code.
|
// Redirect redirects the request to a provided URL with status code.
|
||||||
Redirect(code int, url string) error
|
Redirect(code int, url string) error
|
||||||
|
|
||||||
// Error invokes the registered global HTTP error handler. Generally used by middleware.
|
// Error invokes the registered global HTTP error handler. Generally used by middleware.
|
||||||
// A side-effect of calling global error handler is that now Response has been committed (sent to the client) and
|
// A side-effect of calling global error handler is that now Response has been committed (sent to the client) and
|
||||||
// middlewares up in chain can not change Response status code or Response body anymore.
|
// middlewares up in chain can not change Response status code or Response body anymore.
|
||||||
//
|
//
|
||||||
// Avoid using this method in handlers as no middleware will be able to effectively handle errors after that.
|
// Avoid using this method in handlers as no middleware will be able to effectively handle errors after that.
|
||||||
Error(err error)
|
Error(err error)
|
||||||
|
|
||||||
// Handler returns the matched handler by router.
|
// Handler returns the matched handler by router.
|
||||||
Handler() HandlerFunc
|
Handler() HandlerFunc
|
||||||
|
|
||||||
// SetHandler sets the matched handler by router.
|
// SetHandler sets the matched handler by router.
|
||||||
SetHandler(h HandlerFunc)
|
SetHandler(h HandlerFunc)
|
||||||
|
|
||||||
// Logger returns the `Logger` instance.
|
// Logger returns the `Logger` instance.
|
||||||
Logger() Logger
|
Logger() Logger
|
||||||
|
|
||||||
// SetLogger Set the logger
|
// SetLogger Set the logger
|
||||||
SetLogger(l Logger)
|
SetLogger(l Logger)
|
||||||
|
|
||||||
// Echo returns the `Echo` instance.
|
// Echo returns the `Echo` instance.
|
||||||
Echo() *Echo
|
Echo() *Echo
|
||||||
|
|
||||||
// Reset resets the context after request completes. It must be called along
|
// Reset resets the context after request completes. It must be called along
|
||||||
// with `Echo#AcquireContext()` and `Echo#ReleaseContext()`.
|
// with `Echo#AcquireContext()` and `Echo#ReleaseContext()`.
|
||||||
// See `Echo#ServeHTTP()`
|
// See `Echo#ServeHTTP()`
|
||||||
Reset(r *http.Request, w http.ResponseWriter)
|
Reset(r *http.Request, w http.ResponseWriter)
|
||||||
}
|
}
|
||||||
|
|
||||||
context struct {
|
type context struct {
|
||||||
request *http.Request
|
request *http.Request
|
||||||
response *Response
|
response *Response
|
||||||
path string
|
query url.Values
|
||||||
pnames []string
|
echo *Echo
|
||||||
pvalues []string
|
logger Logger
|
||||||
query url.Values
|
|
||||||
handler HandlerFunc
|
store Map
|
||||||
store Map
|
lock sync.RWMutex
|
||||||
echo *Echo
|
|
||||||
logger Logger
|
// following fields are set by Router
|
||||||
lock sync.RWMutex
|
|
||||||
}
|
// path is route path that Router matched. It is empty string where there is no route match.
|
||||||
)
|
// Route registered with RouteNotFound is considered as a match and path therefore is not empty.
|
||||||
|
path string
|
||||||
|
|
||||||
|
// pnames length is tied to param count for the matched route
|
||||||
|
pnames []string
|
||||||
|
|
||||||
|
// Usually echo.Echo is sizing pvalues but there could be user created middlewares that decide to
|
||||||
|
// overwrite parameter by calling SetParamNames + SetParamValues.
|
||||||
|
// When echo.Echo allocated that slice it length/capacity is tied to echo.Echo.maxParam value.
|
||||||
|
//
|
||||||
|
// It is important that pvalues size is always equal or bigger to pnames length.
|
||||||
|
pvalues []string
|
||||||
|
handler HandlerFunc
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// ContextKeyHeaderAllow is set by Router for getting value for `Allow` header in later stages of handler call chain.
|
// ContextKeyHeaderAllow is set by Router for getting value for `Allow` header in later stages of handler call chain.
|
||||||
@ -329,13 +344,9 @@ func (c *context) SetParamNames(names ...string) {
|
|||||||
c.pnames = names
|
c.pnames = names
|
||||||
|
|
||||||
l := len(names)
|
l := len(names)
|
||||||
if *c.echo.maxParam < l {
|
|
||||||
*c.echo.maxParam = l
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(c.pvalues) < l {
|
if len(c.pvalues) < l {
|
||||||
// Keeping the old pvalues just for backward compatibility, but it sounds that doesn't make sense to keep them,
|
// Keeping the old pvalues just for backward compatibility, but it sounds that doesn't make sense to keep them,
|
||||||
// probably those values will be overriden in a Context#SetParamValues
|
// probably those values will be overridden in a Context#SetParamValues
|
||||||
newPvalues := make([]string, l)
|
newPvalues := make([]string, l)
|
||||||
copy(newPvalues, c.pvalues)
|
copy(newPvalues, c.pvalues)
|
||||||
c.pvalues = newPvalues
|
c.pvalues = newPvalues
|
||||||
@ -347,11 +358,11 @@ func (c *context) ParamValues() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *context) SetParamValues(values ...string) {
|
func (c *context) SetParamValues(values ...string) {
|
||||||
// NOTE: Don't just set c.pvalues = values, because it has to have length c.echo.maxParam at all times
|
// NOTE: Don't just set c.pvalues = values, because it has to have length c.echo.maxParam (or bigger) at all times
|
||||||
// It will brake the Router#Find code
|
// It will brake the Router#Find code
|
||||||
limit := len(values)
|
limit := len(values)
|
||||||
if limit > *c.echo.maxParam {
|
if limit > len(c.pvalues) {
|
||||||
limit = *c.echo.maxParam
|
c.pvalues = make([]string, limit)
|
||||||
}
|
}
|
||||||
for i := 0; i < limit; i++ {
|
for i := 0; i < limit; i++ {
|
||||||
c.pvalues[i] = values[i]
|
c.pvalues[i] = values[i]
|
||||||
@ -489,7 +500,7 @@ func (c *context) jsonPBlob(code int, callback string, i interface{}) (err error
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *context) json(code int, i interface{}, indent string) error {
|
func (c *context) json(code int, i interface{}, indent string) error {
|
||||||
c.writeContentType(MIMEApplicationJSONCharsetUTF8)
|
c.writeContentType(MIMEApplicationJSON)
|
||||||
c.response.Status = code
|
c.response.Status = code
|
||||||
return c.echo.JSONSerializer.Serialize(c, i, indent)
|
return c.echo.JSONSerializer.Serialize(c, i, indent)
|
||||||
}
|
}
|
||||||
@ -507,7 +518,7 @@ func (c *context) JSONPretty(code int, i interface{}, indent string) (err error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *context) JSONBlob(code int, b []byte) (err error) {
|
func (c *context) JSONBlob(code int, b []byte) (err error) {
|
||||||
return c.Blob(code, MIMEApplicationJSONCharsetUTF8, b)
|
return c.Blob(code, MIMEApplicationJSON, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *context) JSONP(code int, callback string, i interface{}) (err error) {
|
func (c *context) JSONP(code int, callback string, i interface{}) (err error) {
|
||||||
@ -584,8 +595,10 @@ func (c *context) Inline(file, name string) error {
|
|||||||
return c.contentDisposition(file, name, "inline")
|
return c.contentDisposition(file, name, "inline")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
|
||||||
|
|
||||||
func (c *context) contentDisposition(file, name, dispositionType string) error {
|
func (c *context) contentDisposition(file, name, dispositionType string) error {
|
||||||
c.response.Header().Set(HeaderContentDisposition, fmt.Sprintf("%s; filename=%q", dispositionType, name))
|
c.response.Header().Set(HeaderContentDisposition, fmt.Sprintf(`%s; filename="%s"`, dispositionType, quoteEscaper.Replace(name)))
|
||||||
return c.File(file)
|
return c.File(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -640,8 +653,8 @@ func (c *context) Reset(r *http.Request, w http.ResponseWriter) {
|
|||||||
c.path = ""
|
c.path = ""
|
||||||
c.pnames = nil
|
c.pnames = nil
|
||||||
c.logger = nil
|
c.logger = nil
|
||||||
// NOTE: Don't reset because it has to have length c.echo.maxParam at all times
|
// NOTE: Don't reset because it has to have length c.echo.maxParam (or bigger) at all times
|
||||||
for i := 0; i < *c.echo.maxParam; i++ {
|
for i := 0; i < len(c.pvalues); i++ {
|
||||||
c.pvalues[i] = ""
|
c.pvalues[i] = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3
vendor/github.com/labstack/echo/v4/context_fs.go
generated
vendored
3
vendor/github.com/labstack/echo/v4/context_fs.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package echo
|
package echo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
301
vendor/github.com/labstack/echo/v4/echo.go
generated
vendored
301
vendor/github.com/labstack/echo/v4/echo.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Package echo implements high performance, minimalist Go web framework.
|
Package echo implements high performance, minimalist Go web framework.
|
||||||
|
|
||||||
@ -39,6 +42,7 @@ package echo
|
|||||||
import (
|
import (
|
||||||
stdContext "context"
|
stdContext "context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -59,97 +63,95 @@ import (
|
|||||||
"golang.org/x/net/http2/h2c"
|
"golang.org/x/net/http2/h2c"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// Echo is the top-level framework instance.
|
||||||
// Echo is the top-level framework instance.
|
//
|
||||||
//
|
// Goroutine safety: Do not mutate Echo instance fields after server has started. Accessing these
|
||||||
// Goroutine safety: Do not mutate Echo instance fields after server has started. Accessing these
|
// fields from handlers/middlewares and changing field values at the same time leads to data-races.
|
||||||
// fields from handlers/middlewares and changing field values at the same time leads to data-races.
|
// Adding new routes after the server has been started is also not safe!
|
||||||
// Adding new routes after the server has been started is also not safe!
|
type Echo struct {
|
||||||
Echo struct {
|
filesystem
|
||||||
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 bound) without having data races.
|
||||||
// listener address info (on which interface/port was listener binded) without having data races.
|
startupMutex sync.RWMutex
|
||||||
startupMutex sync.RWMutex
|
colorer *color.Color
|
||||||
colorer *color.Color
|
|
||||||
|
|
||||||
// premiddleware are middlewares that are run before routing is done. In case a pre-middleware returns
|
// premiddleware are middlewares that are run before routing is done. In case a pre-middleware returns
|
||||||
// an error the router is not executed and the request will end up in the global error handler.
|
// an error the router is not executed and the request will end up in the global error handler.
|
||||||
premiddleware []MiddlewareFunc
|
premiddleware []MiddlewareFunc
|
||||||
middleware []MiddlewareFunc
|
middleware []MiddlewareFunc
|
||||||
maxParam *int
|
maxParam *int
|
||||||
router *Router
|
router *Router
|
||||||
routers map[string]*Router
|
routers map[string]*Router
|
||||||
pool sync.Pool
|
pool sync.Pool
|
||||||
|
|
||||||
StdLogger *stdLog.Logger
|
StdLogger *stdLog.Logger
|
||||||
Server *http.Server
|
Server *http.Server
|
||||||
TLSServer *http.Server
|
TLSServer *http.Server
|
||||||
Listener net.Listener
|
Listener net.Listener
|
||||||
TLSListener net.Listener
|
TLSListener net.Listener
|
||||||
AutoTLSManager autocert.Manager
|
AutoTLSManager autocert.Manager
|
||||||
DisableHTTP2 bool
|
DisableHTTP2 bool
|
||||||
Debug bool
|
Debug bool
|
||||||
HideBanner bool
|
HideBanner bool
|
||||||
HidePort bool
|
HidePort bool
|
||||||
HTTPErrorHandler HTTPErrorHandler
|
HTTPErrorHandler HTTPErrorHandler
|
||||||
Binder Binder
|
Binder Binder
|
||||||
JSONSerializer JSONSerializer
|
JSONSerializer JSONSerializer
|
||||||
Validator Validator
|
Validator Validator
|
||||||
Renderer Renderer
|
Renderer Renderer
|
||||||
Logger Logger
|
Logger Logger
|
||||||
IPExtractor IPExtractor
|
IPExtractor IPExtractor
|
||||||
ListenerNetwork string
|
ListenerNetwork string
|
||||||
|
|
||||||
// OnAddRouteHandler is called when Echo adds new route to specific host router.
|
// OnAddRouteHandler is called when Echo adds new route to specific host router.
|
||||||
OnAddRouteHandler func(host string, route Route, handler HandlerFunc, middleware []MiddlewareFunc)
|
OnAddRouteHandler func(host string, route Route, handler HandlerFunc, middleware []MiddlewareFunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Route contains a handler and information for matching against requests.
|
// Route contains a handler and information for matching against requests.
|
||||||
Route struct {
|
type Route struct {
|
||||||
Method string `json:"method"`
|
Method string `json:"method"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTPError represents an error that occurred while handling a request.
|
// HTTPError represents an error that occurred while handling a request.
|
||||||
HTTPError struct {
|
type HTTPError struct {
|
||||||
Code int `json:"-"`
|
Code int `json:"-"`
|
||||||
Message interface{} `json:"message"`
|
Message interface{} `json:"message"`
|
||||||
Internal error `json:"-"` // Stores the error returned by an external dependency
|
Internal error `json:"-"` // Stores the error returned by an external dependency
|
||||||
}
|
}
|
||||||
|
|
||||||
// MiddlewareFunc defines a function to process middleware.
|
// MiddlewareFunc defines a function to process middleware.
|
||||||
MiddlewareFunc func(next HandlerFunc) HandlerFunc
|
type MiddlewareFunc func(next HandlerFunc) HandlerFunc
|
||||||
|
|
||||||
// HandlerFunc defines a function to serve HTTP requests.
|
// HandlerFunc defines a function to serve HTTP requests.
|
||||||
HandlerFunc func(c Context) error
|
type HandlerFunc func(c Context) error
|
||||||
|
|
||||||
// HTTPErrorHandler is a centralized HTTP error handler.
|
// HTTPErrorHandler is a centralized HTTP error handler.
|
||||||
HTTPErrorHandler func(err error, c Context)
|
type HTTPErrorHandler func(err error, c Context)
|
||||||
|
|
||||||
// Validator is the interface that wraps the Validate function.
|
// Validator is the interface that wraps the Validate function.
|
||||||
Validator interface {
|
type Validator interface {
|
||||||
Validate(i interface{}) error
|
Validate(i interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSONSerializer is the interface that encodes and decodes JSON to and from interfaces.
|
// JSONSerializer is the interface that encodes and decodes JSON to and from interfaces.
|
||||||
JSONSerializer interface {
|
type JSONSerializer interface {
|
||||||
Serialize(c Context, i interface{}, indent string) error
|
Serialize(c Context, i interface{}, indent string) error
|
||||||
Deserialize(c Context, i interface{}) error
|
Deserialize(c Context, i interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renderer is the interface that wraps the Render function.
|
// Renderer is the interface that wraps the Render function.
|
||||||
Renderer interface {
|
type Renderer interface {
|
||||||
Render(io.Writer, string, interface{}, Context) error
|
Render(io.Writer, string, interface{}, Context) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map defines a generic map of type `map[string]interface{}`.
|
// Map defines a generic map of type `map[string]interface{}`.
|
||||||
Map map[string]interface{}
|
type Map map[string]interface{}
|
||||||
|
|
||||||
// Common struct for Echo & Group.
|
// Common struct for Echo & Group.
|
||||||
common struct{}
|
type common struct{}
|
||||||
)
|
|
||||||
|
|
||||||
// HTTP methods
|
// HTTP methods
|
||||||
// NOTE: Deprecated, please use the stdlib constants directly instead.
|
// NOTE: Deprecated, please use the stdlib constants directly instead.
|
||||||
@ -168,7 +170,12 @@ const (
|
|||||||
|
|
||||||
// MIME types
|
// MIME types
|
||||||
const (
|
const (
|
||||||
MIMEApplicationJSON = "application/json"
|
// MIMEApplicationJSON JavaScript Object Notation (JSON) https://www.rfc-editor.org/rfc/rfc8259
|
||||||
|
MIMEApplicationJSON = "application/json"
|
||||||
|
// Deprecated: Please use MIMEApplicationJSON instead. JSON should be encoded using UTF-8 by default.
|
||||||
|
// No "charset" parameter is defined for this registration.
|
||||||
|
// Adding one really has no effect on compliant recipients.
|
||||||
|
// See RFC 8259, section 8.1. https://datatracker.ietf.org/doc/html/rfc8259#section-8.1
|
||||||
MIMEApplicationJSONCharsetUTF8 = MIMEApplicationJSON + "; " + charsetUTF8
|
MIMEApplicationJSONCharsetUTF8 = MIMEApplicationJSON + "; " + charsetUTF8
|
||||||
MIMEApplicationJavaScript = "application/javascript"
|
MIMEApplicationJavaScript = "application/javascript"
|
||||||
MIMEApplicationJavaScriptCharsetUTF8 = MIMEApplicationJavaScript + "; " + charsetUTF8
|
MIMEApplicationJavaScriptCharsetUTF8 = MIMEApplicationJavaScript + "; " + charsetUTF8
|
||||||
@ -258,7 +265,7 @@ const (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
// Version of Echo
|
// Version of Echo
|
||||||
Version = "4.10.0"
|
Version = "4.12.0"
|
||||||
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 = `
|
||||||
@ -273,60 +280,88 @@ ____________________________________O/_______
|
|||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var methods = [...]string{
|
||||||
methods = [...]string{
|
http.MethodConnect,
|
||||||
http.MethodConnect,
|
http.MethodDelete,
|
||||||
http.MethodDelete,
|
http.MethodGet,
|
||||||
http.MethodGet,
|
http.MethodHead,
|
||||||
http.MethodHead,
|
http.MethodOptions,
|
||||||
http.MethodOptions,
|
http.MethodPatch,
|
||||||
http.MethodPatch,
|
http.MethodPost,
|
||||||
http.MethodPost,
|
PROPFIND,
|
||||||
PROPFIND,
|
http.MethodPut,
|
||||||
http.MethodPut,
|
http.MethodTrace,
|
||||||
http.MethodTrace,
|
REPORT,
|
||||||
REPORT,
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Errors
|
// Errors
|
||||||
var (
|
var (
|
||||||
ErrUnsupportedMediaType = NewHTTPError(http.StatusUnsupportedMediaType)
|
ErrBadRequest = NewHTTPError(http.StatusBadRequest) // HTTP 400 Bad Request
|
||||||
ErrNotFound = NewHTTPError(http.StatusNotFound)
|
ErrUnauthorized = NewHTTPError(http.StatusUnauthorized) // HTTP 401 Unauthorized
|
||||||
ErrUnauthorized = NewHTTPError(http.StatusUnauthorized)
|
ErrPaymentRequired = NewHTTPError(http.StatusPaymentRequired) // HTTP 402 Payment Required
|
||||||
ErrForbidden = NewHTTPError(http.StatusForbidden)
|
ErrForbidden = NewHTTPError(http.StatusForbidden) // HTTP 403 Forbidden
|
||||||
ErrMethodNotAllowed = NewHTTPError(http.StatusMethodNotAllowed)
|
ErrNotFound = NewHTTPError(http.StatusNotFound) // HTTP 404 Not Found
|
||||||
ErrStatusRequestEntityTooLarge = NewHTTPError(http.StatusRequestEntityTooLarge)
|
ErrMethodNotAllowed = NewHTTPError(http.StatusMethodNotAllowed) // HTTP 405 Method Not Allowed
|
||||||
ErrTooManyRequests = NewHTTPError(http.StatusTooManyRequests)
|
ErrNotAcceptable = NewHTTPError(http.StatusNotAcceptable) // HTTP 406 Not Acceptable
|
||||||
ErrBadRequest = NewHTTPError(http.StatusBadRequest)
|
ErrProxyAuthRequired = NewHTTPError(http.StatusProxyAuthRequired) // HTTP 407 Proxy AuthRequired
|
||||||
ErrBadGateway = NewHTTPError(http.StatusBadGateway)
|
ErrRequestTimeout = NewHTTPError(http.StatusRequestTimeout) // HTTP 408 Request Timeout
|
||||||
ErrInternalServerError = NewHTTPError(http.StatusInternalServerError)
|
ErrConflict = NewHTTPError(http.StatusConflict) // HTTP 409 Conflict
|
||||||
ErrRequestTimeout = NewHTTPError(http.StatusRequestTimeout)
|
ErrGone = NewHTTPError(http.StatusGone) // HTTP 410 Gone
|
||||||
ErrServiceUnavailable = NewHTTPError(http.StatusServiceUnavailable)
|
ErrLengthRequired = NewHTTPError(http.StatusLengthRequired) // HTTP 411 Length Required
|
||||||
ErrValidatorNotRegistered = errors.New("validator not registered")
|
ErrPreconditionFailed = NewHTTPError(http.StatusPreconditionFailed) // HTTP 412 Precondition Failed
|
||||||
ErrRendererNotRegistered = errors.New("renderer not registered")
|
ErrStatusRequestEntityTooLarge = NewHTTPError(http.StatusRequestEntityTooLarge) // HTTP 413 Payload Too Large
|
||||||
ErrInvalidRedirectCode = errors.New("invalid redirect status code")
|
ErrRequestURITooLong = NewHTTPError(http.StatusRequestURITooLong) // HTTP 414 URI Too Long
|
||||||
ErrCookieNotFound = errors.New("cookie not found")
|
ErrUnsupportedMediaType = NewHTTPError(http.StatusUnsupportedMediaType) // HTTP 415 Unsupported Media Type
|
||||||
ErrInvalidCertOrKeyType = errors.New("invalid cert or key type, must be string or []byte")
|
ErrRequestedRangeNotSatisfiable = NewHTTPError(http.StatusRequestedRangeNotSatisfiable) // HTTP 416 Range Not Satisfiable
|
||||||
ErrInvalidListenerNetwork = errors.New("invalid listener network")
|
ErrExpectationFailed = NewHTTPError(http.StatusExpectationFailed) // HTTP 417 Expectation Failed
|
||||||
|
ErrTeapot = NewHTTPError(http.StatusTeapot) // HTTP 418 I'm a teapot
|
||||||
|
ErrMisdirectedRequest = NewHTTPError(http.StatusMisdirectedRequest) // HTTP 421 Misdirected Request
|
||||||
|
ErrUnprocessableEntity = NewHTTPError(http.StatusUnprocessableEntity) // HTTP 422 Unprocessable Entity
|
||||||
|
ErrLocked = NewHTTPError(http.StatusLocked) // HTTP 423 Locked
|
||||||
|
ErrFailedDependency = NewHTTPError(http.StatusFailedDependency) // HTTP 424 Failed Dependency
|
||||||
|
ErrTooEarly = NewHTTPError(http.StatusTooEarly) // HTTP 425 Too Early
|
||||||
|
ErrUpgradeRequired = NewHTTPError(http.StatusUpgradeRequired) // HTTP 426 Upgrade Required
|
||||||
|
ErrPreconditionRequired = NewHTTPError(http.StatusPreconditionRequired) // HTTP 428 Precondition Required
|
||||||
|
ErrTooManyRequests = NewHTTPError(http.StatusTooManyRequests) // HTTP 429 Too Many Requests
|
||||||
|
ErrRequestHeaderFieldsTooLarge = NewHTTPError(http.StatusRequestHeaderFieldsTooLarge) // HTTP 431 Request Header Fields Too Large
|
||||||
|
ErrUnavailableForLegalReasons = NewHTTPError(http.StatusUnavailableForLegalReasons) // HTTP 451 Unavailable For Legal Reasons
|
||||||
|
ErrInternalServerError = NewHTTPError(http.StatusInternalServerError) // HTTP 500 Internal Server Error
|
||||||
|
ErrNotImplemented = NewHTTPError(http.StatusNotImplemented) // HTTP 501 Not Implemented
|
||||||
|
ErrBadGateway = NewHTTPError(http.StatusBadGateway) // HTTP 502 Bad Gateway
|
||||||
|
ErrServiceUnavailable = NewHTTPError(http.StatusServiceUnavailable) // HTTP 503 Service Unavailable
|
||||||
|
ErrGatewayTimeout = NewHTTPError(http.StatusGatewayTimeout) // HTTP 504 Gateway Timeout
|
||||||
|
ErrHTTPVersionNotSupported = NewHTTPError(http.StatusHTTPVersionNotSupported) // HTTP 505 HTTP Version Not Supported
|
||||||
|
ErrVariantAlsoNegotiates = NewHTTPError(http.StatusVariantAlsoNegotiates) // HTTP 506 Variant Also Negotiates
|
||||||
|
ErrInsufficientStorage = NewHTTPError(http.StatusInsufficientStorage) // HTTP 507 Insufficient Storage
|
||||||
|
ErrLoopDetected = NewHTTPError(http.StatusLoopDetected) // HTTP 508 Loop Detected
|
||||||
|
ErrNotExtended = NewHTTPError(http.StatusNotExtended) // HTTP 510 Not Extended
|
||||||
|
ErrNetworkAuthenticationRequired = NewHTTPError(http.StatusNetworkAuthenticationRequired) // HTTP 511 Network Authentication Required
|
||||||
|
|
||||||
|
ErrValidatorNotRegistered = errors.New("validator not registered")
|
||||||
|
ErrRendererNotRegistered = errors.New("renderer not registered")
|
||||||
|
ErrInvalidRedirectCode = errors.New("invalid redirect status code")
|
||||||
|
ErrCookieNotFound = errors.New("cookie not found")
|
||||||
|
ErrInvalidCertOrKeyType = errors.New("invalid cert or key type, must be string or []byte")
|
||||||
|
ErrInvalidListenerNetwork = errors.New("invalid listener network")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Error handlers
|
// NotFoundHandler is the handler that router uses in case there was no matching route found. Returns an error that results
|
||||||
var (
|
// HTTP 404 status code.
|
||||||
NotFoundHandler = func(c Context) error {
|
var NotFoundHandler = func(c Context) error {
|
||||||
return ErrNotFound
|
return ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
MethodNotAllowedHandler = func(c Context) error {
|
// MethodNotAllowedHandler is the handler thar router uses in case there was no matching route found but there was
|
||||||
// See RFC 7231 section 7.4.1: An origin server MUST generate an Allow field in a 405 (Method Not Allowed)
|
// another matching routes for that requested URL. Returns an error that results HTTP 405 Method Not Allowed status code.
|
||||||
// response and MAY do so in any other response. For disabled resources an empty Allow header may be returned
|
var MethodNotAllowedHandler = func(c Context) error {
|
||||||
routerAllowMethods, ok := c.Get(ContextKeyHeaderAllow).(string)
|
// See RFC 7231 section 7.4.1: An origin server MUST generate an Allow field in a 405 (Method Not Allowed)
|
||||||
if ok && routerAllowMethods != "" {
|
// response and MAY do so in any other response. For disabled resources an empty Allow header may be returned
|
||||||
c.Response().Header().Set(HeaderAllow, routerAllowMethods)
|
routerAllowMethods, ok := c.Get(ContextKeyHeaderAllow).(string)
|
||||||
}
|
if ok && routerAllowMethods != "" {
|
||||||
return ErrMethodNotAllowed
|
c.Response().Header().Set(HeaderAllow, routerAllowMethods)
|
||||||
}
|
}
|
||||||
)
|
return ErrMethodNotAllowed
|
||||||
|
}
|
||||||
|
|
||||||
// New creates an instance of Echo.
|
// New creates an instance of Echo.
|
||||||
func New() (e *Echo) {
|
func New() (e *Echo) {
|
||||||
@ -384,7 +419,7 @@ func (e *Echo) Routers() map[string]*Router {
|
|||||||
//
|
//
|
||||||
// NOTE: In case errors happens in middleware call-chain that is returning from handler (which did not return an error).
|
// NOTE: In case errors happens in middleware call-chain that is returning from handler (which did not return an error).
|
||||||
// When handler has already sent response (ala c.JSON()) and there is error in middleware that is returning from
|
// When handler has already sent response (ala c.JSON()) and there is error in middleware that is returning from
|
||||||
// handler. Then the error that global error handler received will be ignored because we have already "commited" the
|
// handler. Then the error that global error handler received will be ignored because we have already "committed" the
|
||||||
// response and status code header has been sent to the client.
|
// response and status code header has been sent to the client.
|
||||||
func (e *Echo) DefaultHTTPErrorHandler(err error, c Context) {
|
func (e *Echo) DefaultHTTPErrorHandler(err error, c Context) {
|
||||||
|
|
||||||
@ -409,12 +444,18 @@ func (e *Echo) DefaultHTTPErrorHandler(err error, c Context) {
|
|||||||
// Issue #1426
|
// Issue #1426
|
||||||
code := he.Code
|
code := he.Code
|
||||||
message := he.Message
|
message := he.Message
|
||||||
if m, ok := he.Message.(string); ok {
|
|
||||||
|
switch m := he.Message.(type) {
|
||||||
|
case string:
|
||||||
if e.Debug {
|
if e.Debug {
|
||||||
message = Map{"message": m, "error": err.Error()}
|
message = Map{"message": m, "error": err.Error()}
|
||||||
} else {
|
} else {
|
||||||
message = Map{"message": m}
|
message = Map{"message": m}
|
||||||
}
|
}
|
||||||
|
case json.Marshaler:
|
||||||
|
// do nothing - this type knows how to format itself to JSON
|
||||||
|
case error:
|
||||||
|
message = Map{"message": m.Error()}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send response
|
// Send response
|
||||||
@ -585,7 +626,7 @@ func (e *Echo) URL(h HandlerFunc, params ...interface{}) string {
|
|||||||
return e.URI(h, params...)
|
return e.URI(h, params...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reverse generates an URL from route name and provided parameters.
|
// Reverse generates a URL from route name and provided parameters.
|
||||||
func (e *Echo) Reverse(name string, params ...interface{}) string {
|
func (e *Echo) Reverse(name string, params ...interface{}) string {
|
||||||
return e.router.Reverse(name, params...)
|
return e.router.Reverse(name, params...)
|
||||||
}
|
}
|
||||||
|
3
vendor/github.com/labstack/echo/v4/echo_fs.go
generated
vendored
3
vendor/github.com/labstack/echo/v4/echo_fs.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package echo
|
package echo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
35
vendor/github.com/labstack/echo/v4/group.go
generated
vendored
35
vendor/github.com/labstack/echo/v4/group.go
generated
vendored
@ -1,21 +1,22 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package echo
|
package echo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// Group is a set of sub-routes for a specified route. It can be used for inner
|
||||||
// Group is a set of sub-routes for a specified route. It can be used for inner
|
// routes that share a common middleware or functionality that should be separate
|
||||||
// routes that share a common middleware or functionality that should be separate
|
// from the parent echo instance while still inheriting from it.
|
||||||
// from the parent echo instance while still inheriting from it.
|
type Group struct {
|
||||||
Group struct {
|
common
|
||||||
common
|
host string
|
||||||
host string
|
prefix string
|
||||||
prefix string
|
middleware []MiddlewareFunc
|
||||||
middleware []MiddlewareFunc
|
echo *Echo
|
||||||
echo *Echo
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Use implements `Echo#Use()` for sub-routes within the Group.
|
// Use implements `Echo#Use()` for sub-routes within the Group.
|
||||||
func (g *Group) Use(middleware ...MiddlewareFunc) {
|
func (g *Group) Use(middleware ...MiddlewareFunc) {
|
||||||
@ -23,10 +24,12 @@ func (g *Group) Use(middleware ...MiddlewareFunc) {
|
|||||||
if len(g.middleware) == 0 {
|
if len(g.middleware) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Allow all requests to reach the group as they might get dropped if router
|
// group level middlewares are different from Echo `Pre` and `Use` middlewares (those are global). Group level middlewares
|
||||||
// doesn't find a match, making none of the group middleware process.
|
// are only executed if they are added to the Router with route.
|
||||||
g.Any("", NotFoundHandler)
|
// So we register catch all route (404 is a safe way to emulate route match) for this group and now during routing the
|
||||||
g.Any("/*", NotFoundHandler)
|
// Router would find route to match our request path and therefore guarantee the middleware(s) will get executed.
|
||||||
|
g.RouteNotFound("", NotFoundHandler)
|
||||||
|
g.RouteNotFound("/*", NotFoundHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CONNECT implements `Echo#CONNECT()` for sub-routes within the Group.
|
// CONNECT implements `Echo#CONNECT()` for sub-routes within the Group.
|
||||||
|
3
vendor/github.com/labstack/echo/v4/group_fs.go
generated
vendored
3
vendor/github.com/labstack/echo/v4/group_fs.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package echo
|
package echo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
17
vendor/github.com/labstack/echo/v4/ip.go
generated
vendored
17
vendor/github.com/labstack/echo/v4/ip.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package echo
|
package echo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -64,7 +67,7 @@ XFF: "x" "x, a" "x, a, b"
|
|||||||
```
|
```
|
||||||
|
|
||||||
In this case, use **first _untrustable_ IP reading from right**. Never use first one reading from left, as it is
|
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".
|
configurable by client. Here "trustable" means "you are sure the IP address belongs to your infrastructure".
|
||||||
In above example, if `b` and `c` are trustable, the IP address of the client is `a` for both cases, never be `x`.
|
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)`.
|
In Echo, use `ExtractIPFromXFFHeader(...TrustOption)`.
|
||||||
@ -225,15 +228,21 @@ func extractIP(req *http.Request) string {
|
|||||||
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 := extractIP(req)
|
||||||
realIP := req.Header.Get(HeaderXRealIP)
|
realIP := req.Header.Get(HeaderXRealIP)
|
||||||
if realIP != "" {
|
if realIP == "" {
|
||||||
|
return directIP
|
||||||
|
}
|
||||||
|
|
||||||
|
if checker.trust(net.ParseIP(directIP)) {
|
||||||
realIP = strings.TrimPrefix(realIP, "[")
|
realIP = strings.TrimPrefix(realIP, "[")
|
||||||
realIP = strings.TrimSuffix(realIP, "]")
|
realIP = strings.TrimSuffix(realIP, "]")
|
||||||
if ip := net.ParseIP(realIP); ip != nil && checker.trust(ip) {
|
if rIP := net.ParseIP(realIP); rIP != nil {
|
||||||
return realIP
|
return realIP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return extractIP(req)
|
|
||||||
|
return directIP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
3
vendor/github.com/labstack/echo/v4/json.go
generated
vendored
3
vendor/github.com/labstack/echo/v4/json.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package echo
|
package echo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
70
vendor/github.com/labstack/echo/v4/log.go
generated
vendored
70
vendor/github.com/labstack/echo/v4/log.go
generated
vendored
@ -1,41 +1,41 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package echo
|
package echo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/labstack/gommon/log"
|
"github.com/labstack/gommon/log"
|
||||||
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// Logger defines the logging interface.
|
||||||
// Logger defines the logging interface.
|
type Logger interface {
|
||||||
Logger interface {
|
Output() io.Writer
|
||||||
Output() io.Writer
|
SetOutput(w io.Writer)
|
||||||
SetOutput(w io.Writer)
|
Prefix() string
|
||||||
Prefix() string
|
SetPrefix(p string)
|
||||||
SetPrefix(p string)
|
Level() log.Lvl
|
||||||
Level() log.Lvl
|
SetLevel(v log.Lvl)
|
||||||
SetLevel(v log.Lvl)
|
SetHeader(h string)
|
||||||
SetHeader(h string)
|
Print(i ...interface{})
|
||||||
Print(i ...interface{})
|
Printf(format string, args ...interface{})
|
||||||
Printf(format string, args ...interface{})
|
Printj(j log.JSON)
|
||||||
Printj(j log.JSON)
|
Debug(i ...interface{})
|
||||||
Debug(i ...interface{})
|
Debugf(format string, args ...interface{})
|
||||||
Debugf(format string, args ...interface{})
|
Debugj(j log.JSON)
|
||||||
Debugj(j log.JSON)
|
Info(i ...interface{})
|
||||||
Info(i ...interface{})
|
Infof(format string, args ...interface{})
|
||||||
Infof(format string, args ...interface{})
|
Infoj(j log.JSON)
|
||||||
Infoj(j log.JSON)
|
Warn(i ...interface{})
|
||||||
Warn(i ...interface{})
|
Warnf(format string, args ...interface{})
|
||||||
Warnf(format string, args ...interface{})
|
Warnj(j log.JSON)
|
||||||
Warnj(j log.JSON)
|
Error(i ...interface{})
|
||||||
Error(i ...interface{})
|
Errorf(format string, args ...interface{})
|
||||||
Errorf(format string, args ...interface{})
|
Errorj(j log.JSON)
|
||||||
Errorj(j log.JSON)
|
Fatal(i ...interface{})
|
||||||
Fatal(i ...interface{})
|
Fatalj(j log.JSON)
|
||||||
Fatalj(j log.JSON)
|
Fatalf(format string, args ...interface{})
|
||||||
Fatalf(format string, args ...interface{})
|
Panic(i ...interface{})
|
||||||
Panic(i ...interface{})
|
Panicj(j log.JSON)
|
||||||
Panicj(j log.JSON)
|
Panicf(format string, args ...interface{})
|
||||||
Panicf(format string, args ...interface{})
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
|
47
vendor/github.com/labstack/echo/v4/middleware/basic_auth.go
generated
vendored
47
vendor/github.com/labstack/echo/v4/middleware/basic_auth.go
generated
vendored
@ -1,45 +1,46 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// BasicAuthConfig defines the config for BasicAuth middleware.
|
||||||
// BasicAuthConfig defines the config for BasicAuth middleware.
|
type BasicAuthConfig struct {
|
||||||
BasicAuthConfig struct {
|
// Skipper defines a function to skip middleware.
|
||||||
// Skipper defines a function to skip middleware.
|
Skipper Skipper
|
||||||
Skipper Skipper
|
|
||||||
|
|
||||||
// Validator is a function to validate BasicAuth credentials.
|
// Validator is a function to validate BasicAuth credentials.
|
||||||
// Required.
|
// Required.
|
||||||
Validator BasicAuthValidator
|
Validator BasicAuthValidator
|
||||||
|
|
||||||
// Realm is a string to define realm attribute of BasicAuth.
|
// Realm is a string to define realm attribute of BasicAuth.
|
||||||
// Default value "Restricted".
|
// Default value "Restricted".
|
||||||
Realm string
|
Realm string
|
||||||
}
|
}
|
||||||
|
|
||||||
// BasicAuthValidator defines a function to validate BasicAuth credentials.
|
// BasicAuthValidator defines a function to validate BasicAuth credentials.
|
||||||
BasicAuthValidator func(string, string, echo.Context) (bool, error)
|
// The function should return a boolean indicating whether the credentials are valid,
|
||||||
)
|
// and an error if any error occurs during the validation process.
|
||||||
|
type BasicAuthValidator func(string, string, echo.Context) (bool, error)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
basic = "basic"
|
basic = "basic"
|
||||||
defaultRealm = "Restricted"
|
defaultRealm = "Restricted"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// DefaultBasicAuthConfig is the default BasicAuth middleware config.
|
||||||
// DefaultBasicAuthConfig is the default BasicAuth middleware config.
|
var DefaultBasicAuthConfig = BasicAuthConfig{
|
||||||
DefaultBasicAuthConfig = BasicAuthConfig{
|
Skipper: DefaultSkipper,
|
||||||
Skipper: DefaultSkipper,
|
Realm: defaultRealm,
|
||||||
Realm: defaultRealm,
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// BasicAuth returns an BasicAuth middleware.
|
// BasicAuth returns an BasicAuth middleware.
|
||||||
//
|
//
|
||||||
|
55
vendor/github.com/labstack/echo/v4/middleware/body_dump.go
generated
vendored
55
vendor/github.com/labstack/echo/v4/middleware/body_dump.go
generated
vendored
@ -1,8 +1,12 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -10,32 +14,28 @@ import (
|
|||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// BodyDumpConfig defines the config for BodyDump middleware.
|
||||||
// BodyDumpConfig defines the config for BodyDump middleware.
|
type BodyDumpConfig struct {
|
||||||
BodyDumpConfig struct {
|
// Skipper defines a function to skip middleware.
|
||||||
// Skipper defines a function to skip middleware.
|
Skipper Skipper
|
||||||
Skipper Skipper
|
|
||||||
|
|
||||||
// Handler receives request and response payload.
|
// Handler receives request and response payload.
|
||||||
// Required.
|
// Required.
|
||||||
Handler BodyDumpHandler
|
Handler BodyDumpHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
// BodyDumpHandler receives the request and response payload.
|
// BodyDumpHandler receives the request and response payload.
|
||||||
BodyDumpHandler func(echo.Context, []byte, []byte)
|
type BodyDumpHandler func(echo.Context, []byte, []byte)
|
||||||
|
|
||||||
bodyDumpResponseWriter struct {
|
type bodyDumpResponseWriter struct {
|
||||||
io.Writer
|
io.Writer
|
||||||
http.ResponseWriter
|
http.ResponseWriter
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
// DefaultBodyDumpConfig is the default BodyDump middleware config.
|
||||||
// DefaultBodyDumpConfig is the default BodyDump middleware config.
|
var DefaultBodyDumpConfig = BodyDumpConfig{
|
||||||
DefaultBodyDumpConfig = BodyDumpConfig{
|
Skipper: DefaultSkipper,
|
||||||
Skipper: DefaultSkipper,
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// BodyDump returns a BodyDump middleware.
|
// BodyDump returns a BodyDump middleware.
|
||||||
//
|
//
|
||||||
@ -98,9 +98,16 @@ func (w *bodyDumpResponseWriter) Write(b []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *bodyDumpResponseWriter) Flush() {
|
func (w *bodyDumpResponseWriter) Flush() {
|
||||||
w.ResponseWriter.(http.Flusher).Flush()
|
err := responseControllerFlush(w.ResponseWriter)
|
||||||
|
if err != nil && errors.Is(err, http.ErrNotSupported) {
|
||||||
|
panic(errors.New("response writer flushing is not supported"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *bodyDumpResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
func (w *bodyDumpResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||||
return w.ResponseWriter.(http.Hijacker).Hijack()
|
return responseControllerHijack(w.ResponseWriter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *bodyDumpResponseWriter) Unwrap() http.ResponseWriter {
|
||||||
|
return w.ResponseWriter
|
||||||
}
|
}
|
||||||
|
49
vendor/github.com/labstack/echo/v4/middleware/body_limit.go
generated
vendored
49
vendor/github.com/labstack/echo/v4/middleware/body_limit.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -9,32 +12,27 @@ import (
|
|||||||
"github.com/labstack/gommon/bytes"
|
"github.com/labstack/gommon/bytes"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// BodyLimitConfig defines the config for BodyLimit middleware.
|
||||||
// BodyLimitConfig defines the config for BodyLimit middleware.
|
type BodyLimitConfig struct {
|
||||||
BodyLimitConfig struct {
|
// Skipper defines a function to skip middleware.
|
||||||
// Skipper defines a function to skip middleware.
|
Skipper Skipper
|
||||||
Skipper Skipper
|
|
||||||
|
|
||||||
// Maximum allowed size for a request body, it can be specified
|
// Maximum allowed size for a request body, it can be specified
|
||||||
// as `4x` or `4xB`, where x is one of the multiple from K, M, G, T or P.
|
// as `4x` or `4xB`, where x is one of the multiple from K, M, G, T or P.
|
||||||
Limit string `yaml:"limit"`
|
Limit string `yaml:"limit"`
|
||||||
limit int64
|
limit int64
|
||||||
}
|
}
|
||||||
|
|
||||||
limitedReader struct {
|
type limitedReader struct {
|
||||||
BodyLimitConfig
|
BodyLimitConfig
|
||||||
reader io.ReadCloser
|
reader io.ReadCloser
|
||||||
read int64
|
read int64
|
||||||
context echo.Context
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
// DefaultBodyLimitConfig is the default BodyLimit middleware config.
|
||||||
// DefaultBodyLimitConfig is the default BodyLimit middleware config.
|
var DefaultBodyLimitConfig = BodyLimitConfig{
|
||||||
DefaultBodyLimitConfig = BodyLimitConfig{
|
Skipper: DefaultSkipper,
|
||||||
Skipper: DefaultSkipper,
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// BodyLimit returns a BodyLimit middleware.
|
// BodyLimit returns a BodyLimit middleware.
|
||||||
//
|
//
|
||||||
@ -80,7 +78,7 @@ func BodyLimitWithConfig(config BodyLimitConfig) echo.MiddlewareFunc {
|
|||||||
|
|
||||||
// Based on content read
|
// Based on content read
|
||||||
r := pool.Get().(*limitedReader)
|
r := pool.Get().(*limitedReader)
|
||||||
r.Reset(req.Body, c)
|
r.Reset(req.Body)
|
||||||
defer pool.Put(r)
|
defer pool.Put(r)
|
||||||
req.Body = r
|
req.Body = r
|
||||||
|
|
||||||
@ -102,9 +100,8 @@ func (r *limitedReader) Close() error {
|
|||||||
return r.reader.Close()
|
return r.reader.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *limitedReader) Reset(reader io.ReadCloser, context echo.Context) {
|
func (r *limitedReader) Reset(reader io.ReadCloser) {
|
||||||
r.reader = reader
|
r.reader = reader
|
||||||
r.context = context
|
|
||||||
r.read = 0
|
r.read = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
144
vendor/github.com/labstack/echo/v4/middleware/compress.go
generated
vendored
144
vendor/github.com/labstack/echo/v4/middleware/compress.go
generated
vendored
@ -1,7 +1,11 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
@ -12,35 +16,50 @@ import (
|
|||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// GzipConfig defines the config for Gzip middleware.
|
||||||
// GzipConfig defines the config for Gzip middleware.
|
type GzipConfig struct {
|
||||||
GzipConfig struct {
|
// Skipper defines a function to skip middleware.
|
||||||
// Skipper defines a function to skip middleware.
|
Skipper Skipper
|
||||||
Skipper Skipper
|
|
||||||
|
|
||||||
// Gzip compression level.
|
// Gzip compression level.
|
||||||
// Optional. Default value -1.
|
// Optional. Default value -1.
|
||||||
Level int `yaml:"level"`
|
Level int `yaml:"level"`
|
||||||
}
|
|
||||||
|
|
||||||
gzipResponseWriter struct {
|
// Length threshold before gzip compression is applied.
|
||||||
io.Writer
|
// Optional. Default value 0.
|
||||||
http.ResponseWriter
|
//
|
||||||
wroteBody bool
|
// Most of the time you will not need to change the default. Compressing
|
||||||
}
|
// a short response might increase the transmitted data because of the
|
||||||
)
|
// gzip format overhead. Compressing the response will also consume CPU
|
||||||
|
// and time on the server and the client (for decompressing). Depending on
|
||||||
|
// your use case such a threshold might be useful.
|
||||||
|
//
|
||||||
|
// See also:
|
||||||
|
// https://webmasters.stackexchange.com/questions/31750/what-is-recommended-minimum-object-size-for-gzip-performance-benefits
|
||||||
|
MinLength int
|
||||||
|
}
|
||||||
|
|
||||||
|
type gzipResponseWriter struct {
|
||||||
|
io.Writer
|
||||||
|
http.ResponseWriter
|
||||||
|
wroteHeader bool
|
||||||
|
wroteBody bool
|
||||||
|
minLength int
|
||||||
|
minLengthExceeded bool
|
||||||
|
buffer *bytes.Buffer
|
||||||
|
code int
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
gzipScheme = "gzip"
|
gzipScheme = "gzip"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// DefaultGzipConfig is the default Gzip middleware config.
|
||||||
// DefaultGzipConfig is the default Gzip middleware config.
|
var DefaultGzipConfig = GzipConfig{
|
||||||
DefaultGzipConfig = GzipConfig{
|
Skipper: DefaultSkipper,
|
||||||
Skipper: DefaultSkipper,
|
Level: -1,
|
||||||
Level: -1,
|
MinLength: 0,
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
// Gzip returns a middleware which compresses HTTP response using gzip compression
|
// Gzip returns a middleware which compresses HTTP response using gzip compression
|
||||||
// scheme.
|
// scheme.
|
||||||
@ -58,8 +77,12 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
|
|||||||
if config.Level == 0 {
|
if config.Level == 0 {
|
||||||
config.Level = DefaultGzipConfig.Level
|
config.Level = DefaultGzipConfig.Level
|
||||||
}
|
}
|
||||||
|
if config.MinLength < 0 {
|
||||||
|
config.MinLength = DefaultGzipConfig.MinLength
|
||||||
|
}
|
||||||
|
|
||||||
pool := gzipCompressPool(config)
|
pool := gzipCompressPool(config)
|
||||||
|
bpool := bufferPool()
|
||||||
|
|
||||||
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 {
|
||||||
@ -70,7 +93,6 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
|
|||||||
res := c.Response()
|
res := c.Response()
|
||||||
res.Header().Add(echo.HeaderVary, echo.HeaderAcceptEncoding)
|
res.Header().Add(echo.HeaderVary, echo.HeaderAcceptEncoding)
|
||||||
if strings.Contains(c.Request().Header.Get(echo.HeaderAcceptEncoding), gzipScheme) {
|
if strings.Contains(c.Request().Header.Get(echo.HeaderAcceptEncoding), gzipScheme) {
|
||||||
res.Header().Set(echo.HeaderContentEncoding, gzipScheme) // Issue #806
|
|
||||||
i := pool.Get()
|
i := pool.Get()
|
||||||
w, ok := i.(*gzip.Writer)
|
w, ok := i.(*gzip.Writer)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -78,19 +100,38 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
|
|||||||
}
|
}
|
||||||
rw := res.Writer
|
rw := res.Writer
|
||||||
w.Reset(rw)
|
w.Reset(rw)
|
||||||
grw := &gzipResponseWriter{Writer: w, ResponseWriter: rw}
|
|
||||||
|
buf := bpool.Get().(*bytes.Buffer)
|
||||||
|
buf.Reset()
|
||||||
|
|
||||||
|
grw := &gzipResponseWriter{Writer: w, ResponseWriter: rw, minLength: config.MinLength, buffer: buf}
|
||||||
defer func() {
|
defer func() {
|
||||||
|
// There are different reasons for cases when we have not yet written response to the client and now need to do so.
|
||||||
|
// a) handler response had only response code and no response body (ala 404 or redirects etc). Response code need to be written now.
|
||||||
|
// b) body is shorter than our minimum length threshold and being buffered currently and needs to be written
|
||||||
if !grw.wroteBody {
|
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)
|
||||||
}
|
}
|
||||||
|
if grw.wroteHeader {
|
||||||
|
rw.WriteHeader(grw.code)
|
||||||
|
}
|
||||||
// We have to reset response to it's pristine state when
|
// We have to reset response to it's pristine state when
|
||||||
// nothing is written to body or error is returned.
|
// nothing is written to body or error is returned.
|
||||||
// See issue #424, #407.
|
// See issue #424, #407.
|
||||||
res.Writer = rw
|
res.Writer = rw
|
||||||
w.Reset(io.Discard)
|
w.Reset(io.Discard)
|
||||||
|
} else if !grw.minLengthExceeded {
|
||||||
|
// Write uncompressed response
|
||||||
|
res.Writer = rw
|
||||||
|
if grw.wroteHeader {
|
||||||
|
grw.ResponseWriter.WriteHeader(grw.code)
|
||||||
|
}
|
||||||
|
grw.buffer.WriteTo(rw)
|
||||||
|
w.Reset(io.Discard)
|
||||||
}
|
}
|
||||||
w.Close()
|
w.Close()
|
||||||
|
bpool.Put(buf)
|
||||||
pool.Put(w)
|
pool.Put(w)
|
||||||
}()
|
}()
|
||||||
res.Writer = grw
|
res.Writer = grw
|
||||||
@ -102,7 +143,11 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
|
|||||||
|
|
||||||
func (w *gzipResponseWriter) WriteHeader(code int) {
|
func (w *gzipResponseWriter) WriteHeader(code int) {
|
||||||
w.Header().Del(echo.HeaderContentLength) // Issue #444
|
w.Header().Del(echo.HeaderContentLength) // Issue #444
|
||||||
w.ResponseWriter.WriteHeader(code)
|
|
||||||
|
w.wroteHeader = true
|
||||||
|
|
||||||
|
// Delay writing of the header until we know if we'll actually compress the response
|
||||||
|
w.code = code
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *gzipResponseWriter) Write(b []byte) (int, error) {
|
func (w *gzipResponseWriter) Write(b []byte) (int, error) {
|
||||||
@ -110,18 +155,50 @@ func (w *gzipResponseWriter) Write(b []byte) (int, error) {
|
|||||||
w.Header().Set(echo.HeaderContentType, http.DetectContentType(b))
|
w.Header().Set(echo.HeaderContentType, http.DetectContentType(b))
|
||||||
}
|
}
|
||||||
w.wroteBody = true
|
w.wroteBody = true
|
||||||
|
|
||||||
|
if !w.minLengthExceeded {
|
||||||
|
n, err := w.buffer.Write(b)
|
||||||
|
|
||||||
|
if w.buffer.Len() >= w.minLength {
|
||||||
|
w.minLengthExceeded = true
|
||||||
|
|
||||||
|
// The minimum length is exceeded, add Content-Encoding header and write the header
|
||||||
|
w.Header().Set(echo.HeaderContentEncoding, gzipScheme) // Issue #806
|
||||||
|
if w.wroteHeader {
|
||||||
|
w.ResponseWriter.WriteHeader(w.code)
|
||||||
|
}
|
||||||
|
|
||||||
|
return w.Writer.Write(w.buffer.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
return w.Writer.Write(b)
|
return w.Writer.Write(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *gzipResponseWriter) Flush() {
|
func (w *gzipResponseWriter) Flush() {
|
||||||
w.Writer.(*gzip.Writer).Flush()
|
if !w.minLengthExceeded {
|
||||||
if flusher, ok := w.ResponseWriter.(http.Flusher); ok {
|
// Enforce compression because we will not know how much more data will come
|
||||||
flusher.Flush()
|
w.minLengthExceeded = true
|
||||||
|
w.Header().Set(echo.HeaderContentEncoding, gzipScheme) // Issue #806
|
||||||
|
if w.wroteHeader {
|
||||||
|
w.ResponseWriter.WriteHeader(w.code)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Writer.Write(w.buffer.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
w.Writer.(*gzip.Writer).Flush()
|
||||||
|
_ = responseControllerFlush(w.ResponseWriter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *gzipResponseWriter) Unwrap() http.ResponseWriter {
|
||||||
|
return w.ResponseWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *gzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
func (w *gzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||||
return w.ResponseWriter.(http.Hijacker).Hijack()
|
return responseControllerHijack(w.ResponseWriter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *gzipResponseWriter) Push(target string, opts *http.PushOptions) error {
|
func (w *gzipResponseWriter) Push(target string, opts *http.PushOptions) error {
|
||||||
@ -142,3 +219,12 @@ func gzipCompressPool(config GzipConfig) sync.Pool {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func bufferPool() sync.Pool {
|
||||||
|
return sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
return b
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
75
vendor/github.com/labstack/echo/v4/middleware/context_timeout.go
generated
vendored
Normal file
75
vendor/github.com/labstack/echo/v4/middleware/context_timeout.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ContextTimeoutConfig defines the config for ContextTimeout middleware.
|
||||||
|
type ContextTimeoutConfig struct {
|
||||||
|
// Skipper defines a function to skip middleware.
|
||||||
|
Skipper Skipper
|
||||||
|
|
||||||
|
// ErrorHandler is a function when error aries in middleware execution.
|
||||||
|
ErrorHandler func(err error, c echo.Context) error
|
||||||
|
|
||||||
|
// Timeout configures a timeout for the middleware, defaults to 0 for no timeout
|
||||||
|
Timeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContextTimeout returns a middleware which returns error (503 Service Unavailable error) to client
|
||||||
|
// when underlying method returns context.DeadlineExceeded error.
|
||||||
|
func ContextTimeout(timeout time.Duration) echo.MiddlewareFunc {
|
||||||
|
return ContextTimeoutWithConfig(ContextTimeoutConfig{Timeout: timeout})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContextTimeoutWithConfig returns a Timeout middleware with config.
|
||||||
|
func ContextTimeoutWithConfig(config ContextTimeoutConfig) echo.MiddlewareFunc {
|
||||||
|
mw, err := config.ToMiddleware()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return mw
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToMiddleware converts Config to middleware.
|
||||||
|
func (config ContextTimeoutConfig) ToMiddleware() (echo.MiddlewareFunc, error) {
|
||||||
|
if config.Timeout == 0 {
|
||||||
|
return nil, errors.New("timeout must be set")
|
||||||
|
}
|
||||||
|
if config.Skipper == nil {
|
||||||
|
config.Skipper = DefaultSkipper
|
||||||
|
}
|
||||||
|
if config.ErrorHandler == nil {
|
||||||
|
config.ErrorHandler = func(err error, c echo.Context) error {
|
||||||
|
if err != nil && errors.Is(err, context.DeadlineExceeded) {
|
||||||
|
return echo.ErrServiceUnavailable.WithInternal(err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
|
return func(c echo.Context) error {
|
||||||
|
if config.Skipper(c) {
|
||||||
|
return next(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
timeoutContext, cancel := context.WithTimeout(c.Request().Context(), config.Timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
c.SetRequest(c.Request().WithContext(timeoutContext))
|
||||||
|
|
||||||
|
if err := next(c); err != nil {
|
||||||
|
return config.ErrorHandler(err, c)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}, nil
|
||||||
|
}
|
201
vendor/github.com/labstack/echo/v4/middleware/cors.go
generated
vendored
201
vendor/github.com/labstack/echo/v4/middleware/cors.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -9,103 +12,109 @@ import (
|
|||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// CORSConfig defines the config for CORS middleware.
|
||||||
// CORSConfig defines the config for CORS middleware.
|
type CORSConfig struct {
|
||||||
CORSConfig struct {
|
// Skipper defines a function to skip middleware.
|
||||||
// Skipper defines a function to skip middleware.
|
Skipper Skipper
|
||||||
Skipper Skipper
|
|
||||||
|
|
||||||
// AllowOrigins determines the value of the Access-Control-Allow-Origin
|
// AllowOrigins determines the value of the Access-Control-Allow-Origin
|
||||||
// response header. This header defines a list of origins that may access the
|
// response header. This header defines a list of origins that may access the
|
||||||
// resource. The wildcard characters '*' and '?' are supported and are
|
// resource. The wildcard characters '*' and '?' are supported and are
|
||||||
// converted to regex fragments '.*' and '.' accordingly.
|
// converted to regex fragments '.*' and '.' accordingly.
|
||||||
//
|
//
|
||||||
// Security: use extreme caution when handling the origin, and carefully
|
// Security: use extreme caution when handling the origin, and carefully
|
||||||
// validate any logic. Remember that attackers may register hostile domain names.
|
// validate any logic. Remember that attackers may register hostile domain names.
|
||||||
// See https://blog.portswigger.net/2016/10/exploiting-cors-misconfigurations-for.html
|
// See https://blog.portswigger.net/2016/10/exploiting-cors-misconfigurations-for.html
|
||||||
//
|
//
|
||||||
// Optional. Default value []string{"*"}.
|
// Optional. Default value []string{"*"}.
|
||||||
//
|
//
|
||||||
// See also: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
|
// See also: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
|
||||||
AllowOrigins []string `yaml:"allow_origins"`
|
AllowOrigins []string `yaml:"allow_origins"`
|
||||||
|
|
||||||
// AllowOriginFunc is a custom function to validate the origin. It takes the
|
// AllowOriginFunc is a custom function to validate the origin. It takes the
|
||||||
// origin as an argument and returns true if allowed or false otherwise. If
|
// origin as an argument and returns true if allowed or false otherwise. If
|
||||||
// an error is returned, it is returned by the handler. If this option is
|
// an error is returned, it is returned by the handler. If this option is
|
||||||
// set, AllowOrigins is ignored.
|
// set, AllowOrigins is ignored.
|
||||||
//
|
//
|
||||||
// Security: use extreme caution when handling the origin, and carefully
|
// Security: use extreme caution when handling the origin, and carefully
|
||||||
// validate any logic. Remember that attackers may register hostile domain names.
|
// validate any logic. Remember that attackers may register hostile domain names.
|
||||||
// See https://blog.portswigger.net/2016/10/exploiting-cors-misconfigurations-for.html
|
// See https://blog.portswigger.net/2016/10/exploiting-cors-misconfigurations-for.html
|
||||||
//
|
//
|
||||||
// Optional.
|
// Optional.
|
||||||
AllowOriginFunc func(origin string) (bool, error) `yaml:"allow_origin_func"`
|
AllowOriginFunc func(origin string) (bool, error) `yaml:"-"`
|
||||||
|
|
||||||
// AllowMethods determines the value of the Access-Control-Allow-Methods
|
// AllowMethods determines the value of the Access-Control-Allow-Methods
|
||||||
// response header. This header specified the list of methods allowed when
|
// response header. This header specified the list of methods allowed when
|
||||||
// accessing the resource. This is used in response to a preflight request.
|
// accessing the resource. This is used in response to a preflight request.
|
||||||
//
|
//
|
||||||
// Optional. Default value DefaultCORSConfig.AllowMethods.
|
// Optional. Default value DefaultCORSConfig.AllowMethods.
|
||||||
// If `allowMethods` is left empty, this middleware will fill for preflight
|
// If `allowMethods` is left empty, this middleware will fill for preflight
|
||||||
// request `Access-Control-Allow-Methods` header value
|
// request `Access-Control-Allow-Methods` header value
|
||||||
// from `Allow` header that echo.Router set into context.
|
// from `Allow` header that echo.Router set into context.
|
||||||
//
|
//
|
||||||
// See also: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
|
// See also: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
|
||||||
AllowMethods []string `yaml:"allow_methods"`
|
AllowMethods []string `yaml:"allow_methods"`
|
||||||
|
|
||||||
// AllowHeaders determines the value of the Access-Control-Allow-Headers
|
// AllowHeaders determines the value of the Access-Control-Allow-Headers
|
||||||
// response header. This header is used in response to a preflight request to
|
// response header. This header is used in response to a preflight request to
|
||||||
// indicate which HTTP headers can be used when making the actual request.
|
// indicate which HTTP headers can be used when making the actual request.
|
||||||
//
|
//
|
||||||
// Optional. Default value []string{}.
|
// Optional. Default value []string{}.
|
||||||
//
|
//
|
||||||
// See also: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers
|
// See also: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers
|
||||||
AllowHeaders []string `yaml:"allow_headers"`
|
AllowHeaders []string `yaml:"allow_headers"`
|
||||||
|
|
||||||
// AllowCredentials determines the value of the
|
// AllowCredentials determines the value of the
|
||||||
// Access-Control-Allow-Credentials response header. This header indicates
|
// Access-Control-Allow-Credentials response header. This header indicates
|
||||||
// whether or not the response to the request can be exposed when the
|
// whether or not the response to the request can be exposed when the
|
||||||
// credentials mode (Request.credentials) is true. When used as part of a
|
// credentials mode (Request.credentials) is true. When used as part of a
|
||||||
// response to a preflight request, this indicates whether or not the actual
|
// response to a preflight request, this indicates whether or not the actual
|
||||||
// request can be made using credentials. See also
|
// request can be made using credentials. See also
|
||||||
// [MDN: Access-Control-Allow-Credentials].
|
// [MDN: Access-Control-Allow-Credentials].
|
||||||
//
|
//
|
||||||
// Optional. Default value false, in which case the header is not set.
|
// Optional. Default value false, in which case the header is not set.
|
||||||
//
|
//
|
||||||
// Security: avoid using `AllowCredentials = true` with `AllowOrigins = *`.
|
// Security: avoid using `AllowCredentials = true` with `AllowOrigins = *`.
|
||||||
// See "Exploiting CORS misconfigurations for Bitcoins and bounties",
|
// See "Exploiting CORS misconfigurations for Bitcoins and bounties",
|
||||||
// https://blog.portswigger.net/2016/10/exploiting-cors-misconfigurations-for.html
|
// https://blog.portswigger.net/2016/10/exploiting-cors-misconfigurations-for.html
|
||||||
//
|
//
|
||||||
// See also: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
|
// See also: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
|
||||||
AllowCredentials bool `yaml:"allow_credentials"`
|
AllowCredentials bool `yaml:"allow_credentials"`
|
||||||
|
|
||||||
// ExposeHeaders determines the value of Access-Control-Expose-Headers, which
|
// UnsafeWildcardOriginWithAllowCredentials UNSAFE/INSECURE: allows wildcard '*' origin to be used with AllowCredentials
|
||||||
// defines a list of headers that clients are allowed to access.
|
// flag. In that case we consider any origin allowed and send it back to the client with `Access-Control-Allow-Origin` header.
|
||||||
//
|
//
|
||||||
// Optional. Default value []string{}, in which case the header is not set.
|
// This is INSECURE and potentially leads to [cross-origin](https://portswigger.net/research/exploiting-cors-misconfigurations-for-bitcoins-and-bounties)
|
||||||
//
|
// attacks. See: https://github.com/labstack/echo/issues/2400 for discussion on the subject.
|
||||||
// See also: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Header
|
//
|
||||||
ExposeHeaders []string `yaml:"expose_headers"`
|
// Optional. Default value is false.
|
||||||
|
UnsafeWildcardOriginWithAllowCredentials bool `yaml:"unsafe_wildcard_origin_with_allow_credentials"`
|
||||||
|
|
||||||
// MaxAge determines the value of the Access-Control-Max-Age response header.
|
// ExposeHeaders determines the value of Access-Control-Expose-Headers, which
|
||||||
// This header indicates how long (in seconds) the results of a preflight
|
// defines a list of headers that clients are allowed to access.
|
||||||
// request can be cached.
|
//
|
||||||
//
|
// Optional. Default value []string{}, in which case the header is not set.
|
||||||
// Optional. Default value 0. The header is set only if MaxAge > 0.
|
//
|
||||||
//
|
// See also: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Header
|
||||||
// See also: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age
|
ExposeHeaders []string `yaml:"expose_headers"`
|
||||||
MaxAge int `yaml:"max_age"`
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
// MaxAge determines the value of the Access-Control-Max-Age response header.
|
||||||
// DefaultCORSConfig is the default CORS middleware config.
|
// This header indicates how long (in seconds) the results of a preflight
|
||||||
DefaultCORSConfig = CORSConfig{
|
// request can be cached.
|
||||||
Skipper: DefaultSkipper,
|
// The header is set only if MaxAge != 0, negative value sends "0" which instructs browsers not to cache that response.
|
||||||
AllowOrigins: []string{"*"},
|
//
|
||||||
AllowMethods: []string{http.MethodGet, http.MethodHead, http.MethodPut, http.MethodPatch, http.MethodPost, http.MethodDelete},
|
// Optional. Default value 0 - meaning header is not sent.
|
||||||
}
|
//
|
||||||
)
|
// See also: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age
|
||||||
|
MaxAge int `yaml:"max_age"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultCORSConfig is the default CORS middleware config.
|
||||||
|
var DefaultCORSConfig = CORSConfig{
|
||||||
|
Skipper: DefaultSkipper,
|
||||||
|
AllowOrigins: []string{"*"},
|
||||||
|
AllowMethods: []string{http.MethodGet, http.MethodHead, http.MethodPut, http.MethodPatch, http.MethodPost, http.MethodDelete},
|
||||||
|
}
|
||||||
|
|
||||||
// CORS returns a Cross-Origin Resource Sharing (CORS) middleware.
|
// CORS returns a Cross-Origin Resource Sharing (CORS) middleware.
|
||||||
// See also [MDN: Cross-Origin Resource Sharing (CORS)].
|
// See also [MDN: Cross-Origin Resource Sharing (CORS)].
|
||||||
@ -141,8 +150,8 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
|
|||||||
allowOriginPatterns := []string{}
|
allowOriginPatterns := []string{}
|
||||||
for _, origin := range config.AllowOrigins {
|
for _, origin := range config.AllowOrigins {
|
||||||
pattern := regexp.QuoteMeta(origin)
|
pattern := regexp.QuoteMeta(origin)
|
||||||
pattern = strings.Replace(pattern, "\\*", ".*", -1)
|
pattern = strings.ReplaceAll(pattern, "\\*", ".*")
|
||||||
pattern = strings.Replace(pattern, "\\?", ".", -1)
|
pattern = strings.ReplaceAll(pattern, "\\?", ".")
|
||||||
pattern = "^" + pattern + "$"
|
pattern = "^" + pattern + "$"
|
||||||
allowOriginPatterns = append(allowOriginPatterns, pattern)
|
allowOriginPatterns = append(allowOriginPatterns, pattern)
|
||||||
}
|
}
|
||||||
@ -150,7 +159,11 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
|
|||||||
allowMethods := strings.Join(config.AllowMethods, ",")
|
allowMethods := strings.Join(config.AllowMethods, ",")
|
||||||
allowHeaders := strings.Join(config.AllowHeaders, ",")
|
allowHeaders := strings.Join(config.AllowHeaders, ",")
|
||||||
exposeHeaders := strings.Join(config.ExposeHeaders, ",")
|
exposeHeaders := strings.Join(config.ExposeHeaders, ",")
|
||||||
maxAge := strconv.Itoa(config.MaxAge)
|
|
||||||
|
maxAge := "0"
|
||||||
|
if config.MaxAge > 0 {
|
||||||
|
maxAge = strconv.Itoa(config.MaxAge)
|
||||||
|
}
|
||||||
|
|
||||||
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 {
|
||||||
@ -203,7 +216,7 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
|
|||||||
} else {
|
} else {
|
||||||
// Check allowed origins
|
// Check allowed origins
|
||||||
for _, o := range config.AllowOrigins {
|
for _, o := range config.AllowOrigins {
|
||||||
if o == "*" && config.AllowCredentials {
|
if o == "*" && config.AllowCredentials && config.UnsafeWildcardOriginWithAllowCredentials {
|
||||||
allowOrigin = origin
|
allowOrigin = origin
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -273,7 +286,7 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
|
|||||||
res.Header().Set(echo.HeaderAccessControlAllowHeaders, h)
|
res.Header().Set(echo.HeaderAccessControlAllowHeaders, h)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if config.MaxAge > 0 {
|
if config.MaxAge != 0 {
|
||||||
res.Header().Set(echo.HeaderAccessControlMaxAge, maxAge)
|
res.Header().Set(echo.HeaderAccessControlMaxAge, maxAge)
|
||||||
}
|
}
|
||||||
return c.NoContent(http.StatusNoContent)
|
return c.NoContent(http.StatusNoContent)
|
||||||
|
129
vendor/github.com/labstack/echo/v4/middleware/csrf.go
generated
vendored
129
vendor/github.com/labstack/echo/v4/middleware/csrf.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -6,85 +9,80 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"github.com/labstack/gommon/random"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// CSRFConfig defines the config for CSRF middleware.
|
||||||
// CSRFConfig defines the config for CSRF middleware.
|
type CSRFConfig struct {
|
||||||
CSRFConfig struct {
|
// Skipper defines a function to skip middleware.
|
||||||
// Skipper defines a function to skip middleware.
|
Skipper Skipper
|
||||||
Skipper Skipper
|
|
||||||
|
|
||||||
// TokenLength is the length of the generated token.
|
// TokenLength is the length of the generated token.
|
||||||
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>:<name>" or "<source>:<name>,<source>:<name>" 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>" or "header:<name>:<cut-prefix>"
|
// - "header:<name>" or "header:<name>:<cut-prefix>"
|
||||||
// - "query:<name>"
|
// - "query:<name>"
|
||||||
// - "form:<name>"
|
// - "form:<name>"
|
||||||
// Multiple sources example:
|
// Multiple sources example:
|
||||||
// - "header:X-CSRF-Token,query:csrf"
|
// - "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.
|
||||||
// Optional. Default value "csrf".
|
// Optional. Default value "csrf".
|
||||||
ContextKey string `yaml:"context_key"`
|
ContextKey string `yaml:"context_key"`
|
||||||
|
|
||||||
// Name of the CSRF cookie. This cookie will store CSRF token.
|
// Name of the CSRF cookie. This cookie will store CSRF token.
|
||||||
// Optional. Default value "csrf".
|
// Optional. Default value "csrf".
|
||||||
CookieName string `yaml:"cookie_name"`
|
CookieName string `yaml:"cookie_name"`
|
||||||
|
|
||||||
// Domain of the CSRF cookie.
|
// Domain of the CSRF cookie.
|
||||||
// Optional. Default value none.
|
// Optional. Default value none.
|
||||||
CookieDomain string `yaml:"cookie_domain"`
|
CookieDomain string `yaml:"cookie_domain"`
|
||||||
|
|
||||||
// Path of the CSRF cookie.
|
// Path of the CSRF cookie.
|
||||||
// Optional. Default value none.
|
// Optional. Default value none.
|
||||||
CookiePath string `yaml:"cookie_path"`
|
CookiePath string `yaml:"cookie_path"`
|
||||||
|
|
||||||
// Max age (in seconds) of the CSRF cookie.
|
// Max age (in seconds) of the CSRF cookie.
|
||||||
// Optional. Default value 86400 (24hr).
|
// Optional. Default value 86400 (24hr).
|
||||||
CookieMaxAge int `yaml:"cookie_max_age"`
|
CookieMaxAge int `yaml:"cookie_max_age"`
|
||||||
|
|
||||||
// Indicates if CSRF cookie is secure.
|
// Indicates if CSRF cookie is secure.
|
||||||
// Optional. Default value false.
|
// Optional. Default value false.
|
||||||
CookieSecure bool `yaml:"cookie_secure"`
|
CookieSecure bool `yaml:"cookie_secure"`
|
||||||
|
|
||||||
// Indicates if CSRF cookie is HTTP only.
|
// Indicates if CSRF cookie is HTTP only.
|
||||||
// Optional. Default value false.
|
// Optional. Default value false.
|
||||||
CookieHTTPOnly bool `yaml:"cookie_http_only"`
|
CookieHTTPOnly bool `yaml:"cookie_http_only"`
|
||||||
|
|
||||||
// Indicates SameSite mode of the CSRF cookie.
|
// Indicates SameSite mode of the CSRF cookie.
|
||||||
// Optional. Default value SameSiteDefaultMode.
|
// Optional. Default value SameSiteDefaultMode.
|
||||||
CookieSameSite http.SameSite `yaml:"cookie_same_site"`
|
CookieSameSite http.SameSite `yaml:"cookie_same_site"`
|
||||||
|
|
||||||
// ErrorHandler defines a function which is executed for returning custom errors.
|
// ErrorHandler defines a function which is executed for returning custom errors.
|
||||||
ErrorHandler CSRFErrorHandler
|
ErrorHandler CSRFErrorHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
// CSRFErrorHandler is a function which is executed for creating custom errors.
|
// CSRFErrorHandler is a function which is executed for creating custom errors.
|
||||||
CSRFErrorHandler func(err error, c echo.Context) error
|
type CSRFErrorHandler func(err error, c echo.Context) error
|
||||||
)
|
|
||||||
|
|
||||||
// ErrCSRFInvalid is returned when CSRF check fails
|
// ErrCSRFInvalid is returned when CSRF check fails
|
||||||
var ErrCSRFInvalid = echo.NewHTTPError(http.StatusForbidden, "invalid csrf token")
|
var ErrCSRFInvalid = echo.NewHTTPError(http.StatusForbidden, "invalid csrf token")
|
||||||
|
|
||||||
var (
|
// DefaultCSRFConfig is the default CSRF middleware config.
|
||||||
// DefaultCSRFConfig is the default CSRF middleware config.
|
var DefaultCSRFConfig = CSRFConfig{
|
||||||
DefaultCSRFConfig = CSRFConfig{
|
Skipper: DefaultSkipper,
|
||||||
Skipper: DefaultSkipper,
|
TokenLength: 32,
|
||||||
TokenLength: 32,
|
TokenLookup: "header:" + echo.HeaderXCSRFToken,
|
||||||
TokenLookup: "header:" + echo.HeaderXCSRFToken,
|
ContextKey: "csrf",
|
||||||
ContextKey: "csrf",
|
CookieName: "_csrf",
|
||||||
CookieName: "_csrf",
|
CookieMaxAge: 86400,
|
||||||
CookieMaxAge: 86400,
|
CookieSameSite: http.SameSiteDefaultMode,
|
||||||
CookieSameSite: http.SameSiteDefaultMode,
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// CSRF returns a Cross-Site Request Forgery (CSRF) middleware.
|
// CSRF returns a Cross-Site Request Forgery (CSRF) middleware.
|
||||||
// See: https://en.wikipedia.org/wiki/Cross-site_request_forgery
|
// See: https://en.wikipedia.org/wiki/Cross-site_request_forgery
|
||||||
@ -103,6 +101,7 @@ func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc {
|
|||||||
if config.TokenLength == 0 {
|
if config.TokenLength == 0 {
|
||||||
config.TokenLength = DefaultCSRFConfig.TokenLength
|
config.TokenLength = DefaultCSRFConfig.TokenLength
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.TokenLookup == "" {
|
if config.TokenLookup == "" {
|
||||||
config.TokenLookup = DefaultCSRFConfig.TokenLookup
|
config.TokenLookup = DefaultCSRFConfig.TokenLookup
|
||||||
}
|
}
|
||||||
@ -119,9 +118,9 @@ func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc {
|
|||||||
config.CookieSecure = true
|
config.CookieSecure = true
|
||||||
}
|
}
|
||||||
|
|
||||||
extractors, err := CreateExtractors(config.TokenLookup)
|
extractors, cErr := CreateExtractors(config.TokenLookup)
|
||||||
if err != nil {
|
if cErr != nil {
|
||||||
panic(err)
|
panic(cErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
@ -132,7 +131,7 @@ func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc {
|
|||||||
|
|
||||||
token := ""
|
token := ""
|
||||||
if k, err := c.Cookie(config.CookieName); err != nil {
|
if k, err := c.Cookie(config.CookieName); err != nil {
|
||||||
token = random.String(config.TokenLength) // Generate token
|
token = randomString(config.TokenLength)
|
||||||
} else {
|
} else {
|
||||||
token = k.Value // Reuse token
|
token = k.Value // Reuse token
|
||||||
}
|
}
|
||||||
|
37
vendor/github.com/labstack/echo/v4/middleware/decompress.go
generated
vendored
37
vendor/github.com/labstack/echo/v4/middleware/decompress.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -9,18 +12,16 @@ import (
|
|||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// DecompressConfig defines the config for Decompress middleware.
|
||||||
// DecompressConfig defines the config for Decompress middleware.
|
type DecompressConfig struct {
|
||||||
DecompressConfig struct {
|
// Skipper defines a function to skip middleware.
|
||||||
// Skipper defines a function to skip middleware.
|
Skipper Skipper
|
||||||
Skipper Skipper
|
|
||||||
|
|
||||||
// GzipDecompressPool defines an interface to provide the sync.Pool used to create/store Gzip readers
|
// GzipDecompressPool defines an interface to provide the sync.Pool used to create/store Gzip readers
|
||||||
GzipDecompressPool Decompressor
|
GzipDecompressPool Decompressor
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
//GZIPEncoding content-encoding header if set to "gzip", decompress body contents.
|
// GZIPEncoding content-encoding header if set to "gzip", decompress body contents.
|
||||||
const GZIPEncoding string = "gzip"
|
const GZIPEncoding string = "gzip"
|
||||||
|
|
||||||
// Decompressor is used to get the sync.Pool used by the middleware to get Gzip readers
|
// Decompressor is used to get the sync.Pool used by the middleware to get Gzip readers
|
||||||
@ -28,13 +29,11 @@ type Decompressor interface {
|
|||||||
gzipDecompressPool() sync.Pool
|
gzipDecompressPool() sync.Pool
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
// DefaultDecompressConfig defines the config for decompress middleware
|
||||||
//DefaultDecompressConfig defines the config for decompress middleware
|
var DefaultDecompressConfig = DecompressConfig{
|
||||||
DefaultDecompressConfig = DecompressConfig{
|
Skipper: DefaultSkipper,
|
||||||
Skipper: DefaultSkipper,
|
GzipDecompressPool: &DefaultGzipDecompressPool{},
|
||||||
GzipDecompressPool: &DefaultGzipDecompressPool{},
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// DefaultGzipDecompressPool is the default implementation of Decompressor interface
|
// DefaultGzipDecompressPool is the default implementation of Decompressor interface
|
||||||
type DefaultGzipDecompressPool struct {
|
type DefaultGzipDecompressPool struct {
|
||||||
@ -44,12 +43,12 @@ func (d *DefaultGzipDecompressPool) gzipDecompressPool() sync.Pool {
|
|||||||
return sync.Pool{New: func() interface{} { return new(gzip.Reader) }}
|
return sync.Pool{New: func() interface{} { return new(gzip.Reader) }}
|
||||||
}
|
}
|
||||||
|
|
||||||
//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
|
||||||
func Decompress() echo.MiddlewareFunc {
|
func Decompress() echo.MiddlewareFunc {
|
||||||
return DecompressWithConfig(DefaultDecompressConfig)
|
return DecompressWithConfig(DefaultDecompressConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
//DecompressWithConfig decompresses request body based if content encoding type is set to "gzip" with config
|
// DecompressWithConfig decompresses request body based if content encoding type is set to "gzip" with config
|
||||||
func DecompressWithConfig(config DecompressConfig) echo.MiddlewareFunc {
|
func DecompressWithConfig(config DecompressConfig) echo.MiddlewareFunc {
|
||||||
// Defaults
|
// Defaults
|
||||||
if config.Skipper == nil {
|
if config.Skipper == nil {
|
||||||
|
3
vendor/github.com/labstack/echo/v4/middleware/extractor.go
generated
vendored
3
vendor/github.com/labstack/echo/v4/middleware/extractor.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
221
vendor/github.com/labstack/echo/v4/middleware/jwt.go
generated
vendored
221
vendor/github.com/labstack/echo/v4/middleware/jwt.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
//go:build go1.15
|
//go:build go1.15
|
||||||
// +build go1.15
|
// +build go1.15
|
||||||
|
|
||||||
@ -12,139 +15,135 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// JWTConfig defines the config for JWT middleware.
|
||||||
// JWTConfig defines the config for JWT middleware.
|
type JWTConfig struct {
|
||||||
JWTConfig struct {
|
// Skipper defines a function to skip middleware.
|
||||||
// Skipper defines a function to skip middleware.
|
Skipper Skipper
|
||||||
Skipper Skipper
|
|
||||||
|
|
||||||
// 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 before middleware chain continues with next
|
// SuccessHandler defines a function which is executed for a valid token before middleware chain continues with next
|
||||||
// middleware or handler.
|
// 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.
|
||||||
// It may be used to define a custom JWT error.
|
// It may be used to define a custom JWT error.
|
||||||
ErrorHandler JWTErrorHandler
|
ErrorHandler JWTErrorHandler
|
||||||
|
|
||||||
// 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
|
// ContinueOnIgnoredError allows the next middleware/handler to be called when ErrorHandlerWithContext decides to
|
||||||
// ignore the error (by returning `nil`).
|
// 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.
|
// 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
|
// 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.
|
// and continue. Some logic down the remaining execution chain needs to check that (public) token value then.
|
||||||
ContinueOnIgnoredError bool
|
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.
|
||||||
// Required if neither user-defined KeyFunc nor SigningKeys is provided.
|
// Required if neither user-defined KeyFunc nor SigningKeys is provided.
|
||||||
SigningKey interface{}
|
SigningKey interface{}
|
||||||
|
|
||||||
// Map of signing keys to validate token with kid field usage.
|
// Map of signing keys to validate token with kid field usage.
|
||||||
// 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.
|
||||||
// Required if neither user-defined KeyFunc nor SigningKey is provided.
|
// Required if neither user-defined KeyFunc nor SigningKey is provided.
|
||||||
SigningKeys map[string]interface{}
|
SigningKeys map[string]interface{}
|
||||||
|
|
||||||
// Signing method used to check the token's signing algorithm.
|
// Signing method used to check the token's signing algorithm.
|
||||||
// Optional. Default value HS256.
|
// Optional. Default value HS256.
|
||||||
SigningMethod string
|
SigningMethod string
|
||||||
|
|
||||||
// Context key to store user information from the token into context.
|
// Context key to store user information from the token into context.
|
||||||
// Optional. Default value "user".
|
// Optional. Default value "user".
|
||||||
ContextKey string
|
ContextKey string
|
||||||
|
|
||||||
// Claims are extendable claims data defining token content. Used by default ParseTokenFunc implementation.
|
// Claims are extendable claims data defining token content. Used by default ParseTokenFunc implementation.
|
||||||
// Not used if custom ParseTokenFunc is set.
|
// Not used if custom ParseTokenFunc is set.
|
||||||
// Optional. Default value jwt.MapClaims
|
// Optional. Default value jwt.MapClaims
|
||||||
Claims jwt.Claims
|
Claims jwt.Claims
|
||||||
|
|
||||||
// TokenLookup is a string in the form of "<source>:<name>" or "<source>:<name>,<source>:<name>" 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:Authorization".
|
// Optional. Default value "header:Authorization".
|
||||||
// Possible values:
|
// Possible values:
|
||||||
// - "header:<name>" or "header:<name>:<cut-prefix>"
|
// - "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
|
// `<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
|
// 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.
|
// 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 `.
|
// In case of JWT tokens `Authorization: Bearer <token>` prefix we cut is `Bearer `.
|
||||||
// If prefix is left empty the whole value is returned.
|
// 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>"
|
||||||
// Multiple 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.
|
// 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.
|
// This is one of the two options to provide a token extractor.
|
||||||
// The order of precedence is user-defined TokenLookupFuncs, and TokenLookup.
|
// The order of precedence is user-defined TokenLookupFuncs, and TokenLookup.
|
||||||
// You can also provide both if you want.
|
// You can also provide both if you want.
|
||||||
TokenLookupFuncs []ValuesExtractor
|
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
|
||||||
|
|
||||||
// KeyFunc defines a user-defined function that supplies the public key for a token validation.
|
// KeyFunc defines a user-defined function that supplies the public key for a token validation.
|
||||||
// The function shall take care of verifying the signing algorithm and selecting the proper key.
|
// The function shall take care of verifying the signing algorithm and selecting the proper key.
|
||||||
// A user-defined KeyFunc can be useful if tokens are issued by an external party.
|
// A user-defined KeyFunc can be useful if tokens are issued by an external party.
|
||||||
// Used by default ParseTokenFunc implementation.
|
// Used by default ParseTokenFunc implementation.
|
||||||
//
|
//
|
||||||
// When a user-defined KeyFunc is provided, SigningKey, SigningKeys, and SigningMethod are ignored.
|
// When a user-defined KeyFunc is provided, SigningKey, SigningKeys, and SigningMethod are ignored.
|
||||||
// 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.
|
||||||
// Required if neither SigningKeys nor SigningKey is provided.
|
// Required if neither SigningKeys nor SigningKey is provided.
|
||||||
// Not used if custom ParseTokenFunc is set.
|
// Not used if custom ParseTokenFunc is set.
|
||||||
// Default to an internal implementation verifying the signing algorithm and selecting the proper key.
|
// Default to an internal implementation verifying the signing algorithm and selecting the proper key.
|
||||||
KeyFunc jwt.Keyfunc
|
KeyFunc jwt.Keyfunc
|
||||||
|
|
||||||
// ParseTokenFunc defines a user-defined function that parses token from given auth. Returns an error when token
|
// ParseTokenFunc defines a user-defined function that parses token from given auth. Returns an error when token
|
||||||
// parsing fails or parsed token is invalid.
|
// parsing fails or parsed token is invalid.
|
||||||
// Defaults to implementation using `github.com/golang-jwt/jwt` as JWT implementation library
|
// Defaults to implementation using `github.com/golang-jwt/jwt` as JWT implementation library
|
||||||
ParseTokenFunc func(auth string, c echo.Context) (interface{}, error)
|
ParseTokenFunc func(auth string, c echo.Context) (interface{}, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// JWTSuccessHandler defines a function which is executed for a valid token.
|
// JWTSuccessHandler defines a function which is executed for a valid token.
|
||||||
JWTSuccessHandler func(c echo.Context)
|
type 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(err error) error
|
type 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(err error, c echo.Context) error
|
type JWTErrorHandlerWithContext func(err error, c echo.Context) error
|
||||||
)
|
|
||||||
|
|
||||||
// Algorithms
|
// Algorithms
|
||||||
const (
|
const (
|
||||||
AlgorithmHS256 = "HS256"
|
AlgorithmHS256 = "HS256"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Errors
|
// ErrJWTMissing is error that is returned when no JWToken was extracted from the request.
|
||||||
var (
|
var ErrJWTMissing = echo.NewHTTPError(http.StatusBadRequest, "missing or malformed jwt")
|
||||||
ErrJWTMissing = echo.NewHTTPError(http.StatusBadRequest, "missing or malformed jwt")
|
|
||||||
ErrJWTInvalid = echo.NewHTTPError(http.StatusUnauthorized, "invalid or expired jwt")
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
// ErrJWTInvalid is error that is returned when middleware could not parse JWT correctly.
|
||||||
// DefaultJWTConfig is the default JWT auth middleware config.
|
var ErrJWTInvalid = echo.NewHTTPError(http.StatusUnauthorized, "invalid or expired jwt")
|
||||||
DefaultJWTConfig = JWTConfig{
|
|
||||||
Skipper: DefaultSkipper,
|
// DefaultJWTConfig is the default JWT auth middleware config.
|
||||||
SigningMethod: AlgorithmHS256,
|
var DefaultJWTConfig = JWTConfig{
|
||||||
ContextKey: "user",
|
Skipper: DefaultSkipper,
|
||||||
TokenLookup: "header:" + echo.HeaderAuthorization,
|
SigningMethod: AlgorithmHS256,
|
||||||
TokenLookupFuncs: nil,
|
ContextKey: "user",
|
||||||
AuthScheme: "Bearer",
|
TokenLookup: "header:" + echo.HeaderAuthorization,
|
||||||
Claims: jwt.MapClaims{},
|
TokenLookupFuncs: nil,
|
||||||
KeyFunc: nil,
|
AuthScheme: "Bearer",
|
||||||
}
|
Claims: jwt.MapClaims{},
|
||||||
)
|
KeyFunc: nil,
|
||||||
|
}
|
||||||
|
|
||||||
// JWT returns a JSON Web Token (JWT) auth middleware.
|
// JWT returns a JSON Web Token (JWT) auth middleware.
|
||||||
//
|
//
|
||||||
@ -196,9 +195,9 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
|
|||||||
config.ParseTokenFunc = config.defaultParseToken
|
config.ParseTokenFunc = config.defaultParseToken
|
||||||
}
|
}
|
||||||
|
|
||||||
extractors, err := createExtractors(config.TokenLookup, config.AuthScheme)
|
extractors, cErr := createExtractors(config.TokenLookup, config.AuthScheme)
|
||||||
if err != nil {
|
if cErr != nil {
|
||||||
panic(err)
|
panic(cErr)
|
||||||
}
|
}
|
||||||
if len(config.TokenLookupFuncs) > 0 {
|
if len(config.TokenLookupFuncs) > 0 {
|
||||||
extractors = append(config.TokenLookupFuncs, extractors...)
|
extractors = append(config.TokenLookupFuncs, extractors...)
|
||||||
|
105
vendor/github.com/labstack/echo/v4/middleware/key_auth.go
generated
vendored
105
vendor/github.com/labstack/echo/v4/middleware/key_auth.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -6,69 +9,65 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// KeyAuthConfig defines the config for KeyAuth middleware.
|
||||||
// KeyAuthConfig defines the config for KeyAuth middleware.
|
type KeyAuthConfig struct {
|
||||||
KeyAuthConfig struct {
|
// 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>" or "<source>:<name>,<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>" or "header:<name>:<cut-prefix>"
|
// - "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
|
// `<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
|
// 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.
|
// 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 `.
|
// 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>"
|
||||||
// Multiple sources example:
|
// Multiple sources example:
|
||||||
// - "header:Authorization,header:X-Api-Key"
|
// - "header:Authorization,header:X-Api-Key"
|
||||||
KeyLookup string
|
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".
|
||||||
AuthScheme string
|
AuthScheme string
|
||||||
|
|
||||||
// Validator is a function to validate key.
|
// Validator is a function to validate key.
|
||||||
// Required.
|
// Required.
|
||||||
Validator KeyAuthValidator
|
Validator KeyAuthValidator
|
||||||
|
|
||||||
// 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
|
// ContinueOnIgnoredError allows the next middleware/handler to be called when ErrorHandler decides to
|
||||||
// ignore the error (by returning `nil`).
|
// 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.
|
// 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
|
// 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.
|
// and continue. Some logic down the remaining execution chain needs to check that (public) key auth value then.
|
||||||
ContinueOnIgnoredError bool
|
ContinueOnIgnoredError bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// KeyAuthValidator defines a function to validate KeyAuth credentials.
|
// KeyAuthValidator defines a function to validate KeyAuth credentials.
|
||||||
KeyAuthValidator func(auth string, c echo.Context) (bool, error)
|
type KeyAuthValidator func(auth string, c echo.Context) (bool, 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(err error, c echo.Context) error
|
type KeyAuthErrorHandler func(err error, c echo.Context) error
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// DefaultKeyAuthConfig is the default KeyAuth middleware config.
|
|
||||||
DefaultKeyAuthConfig = KeyAuthConfig{
|
|
||||||
Skipper: DefaultSkipper,
|
|
||||||
KeyLookup: "header:" + echo.HeaderAuthorization,
|
|
||||||
AuthScheme: "Bearer",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// ErrKeyAuthMissing is error type when KeyAuth middleware is unable to extract value from lookups
|
// ErrKeyAuthMissing is error type when KeyAuth middleware is unable to extract value from lookups
|
||||||
type ErrKeyAuthMissing struct {
|
type ErrKeyAuthMissing struct {
|
||||||
Err error
|
Err error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DefaultKeyAuthConfig is the default KeyAuth middleware config.
|
||||||
|
var DefaultKeyAuthConfig = KeyAuthConfig{
|
||||||
|
Skipper: DefaultSkipper,
|
||||||
|
KeyLookup: "header:" + echo.HeaderAuthorization,
|
||||||
|
AuthScheme: "Bearer",
|
||||||
|
}
|
||||||
|
|
||||||
// Error returns errors text
|
// Error returns errors text
|
||||||
func (e *ErrKeyAuthMissing) Error() string {
|
func (e *ErrKeyAuthMissing) Error() string {
|
||||||
return e.Err.Error()
|
return e.Err.Error()
|
||||||
@ -108,9 +107,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")
|
||||||
}
|
}
|
||||||
|
|
||||||
extractors, err := createExtractors(config.KeyLookup, config.AuthScheme)
|
extractors, cErr := createExtractors(config.KeyLookup, config.AuthScheme)
|
||||||
if err != nil {
|
if cErr != nil {
|
||||||
panic(err)
|
panic(cErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
|
129
vendor/github.com/labstack/echo/v4/middleware/logger.go
generated
vendored
129
vendor/github.com/labstack/echo/v4/middleware/logger.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -14,77 +17,73 @@ import (
|
|||||||
"github.com/valyala/fasttemplate"
|
"github.com/valyala/fasttemplate"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// LoggerConfig defines the config for Logger middleware.
|
||||||
// LoggerConfig defines the config for Logger middleware.
|
type LoggerConfig struct {
|
||||||
LoggerConfig struct {
|
// Skipper defines a function to skip middleware.
|
||||||
// Skipper defines a function to skip middleware.
|
Skipper Skipper
|
||||||
Skipper Skipper
|
|
||||||
|
|
||||||
// Tags to construct the logger format.
|
// Tags to construct the logger format.
|
||||||
//
|
//
|
||||||
// - time_unix
|
// - time_unix
|
||||||
// - time_unix_milli
|
// - time_unix_milli
|
||||||
// - time_unix_micro
|
// - time_unix_micro
|
||||||
// - time_unix_nano
|
// - time_unix_nano
|
||||||
// - time_rfc3339
|
// - time_rfc3339
|
||||||
// - time_rfc3339_nano
|
// - time_rfc3339_nano
|
||||||
// - time_custom
|
// - time_custom
|
||||||
// - id (Request ID)
|
// - id (Request ID)
|
||||||
// - remote_ip
|
// - remote_ip
|
||||||
// - uri
|
// - uri
|
||||||
// - host
|
// - host
|
||||||
// - method
|
// - method
|
||||||
// - path
|
// - path
|
||||||
// - route
|
// - route
|
||||||
// - protocol
|
// - protocol
|
||||||
// - referer
|
// - referer
|
||||||
// - user_agent
|
// - user_agent
|
||||||
// - status
|
// - status
|
||||||
// - error
|
// - error
|
||||||
// - latency (In nanoseconds)
|
// - latency (In nanoseconds)
|
||||||
// - latency_human (Human readable)
|
// - latency_human (Human readable)
|
||||||
// - bytes_in (Bytes received)
|
// - bytes_in (Bytes received)
|
||||||
// - bytes_out (Bytes sent)
|
// - bytes_out (Bytes sent)
|
||||||
// - header:<NAME>
|
// - header:<NAME>
|
||||||
// - query:<NAME>
|
// - query:<NAME>
|
||||||
// - form:<NAME>
|
// - form:<NAME>
|
||||||
// - custom (see CustomTagFunc field)
|
// - custom (see CustomTagFunc field)
|
||||||
//
|
//
|
||||||
// Example "${remote_ip} ${status}"
|
// Example "${remote_ip} ${status}"
|
||||||
//
|
//
|
||||||
// Optional. Default value DefaultLoggerConfig.Format.
|
// Optional. Default value DefaultLoggerConfig.Format.
|
||||||
Format string `yaml:"format"`
|
Format string `yaml:"format"`
|
||||||
|
|
||||||
// Optional. Default value DefaultLoggerConfig.CustomTimeFormat.
|
// Optional. Default value DefaultLoggerConfig.CustomTimeFormat.
|
||||||
CustomTimeFormat string `yaml:"custom_time_format"`
|
CustomTimeFormat string `yaml:"custom_time_format"`
|
||||||
|
|
||||||
// CustomTagFunc is function called for `${custom}` tag to output user implemented text by writing it to buf.
|
// CustomTagFunc is function called for `${custom}` tag to output user implemented text by writing it to buf.
|
||||||
// Make sure that outputted text creates valid JSON string with other logged tags.
|
// Make sure that outputted text creates valid JSON string with other logged tags.
|
||||||
// Optional.
|
// Optional.
|
||||||
CustomTagFunc func(c echo.Context, buf *bytes.Buffer) (int, error)
|
CustomTagFunc func(c echo.Context, buf *bytes.Buffer) (int, error)
|
||||||
|
|
||||||
// Output is a writer where logs in JSON format are written.
|
// Output is a writer where logs in JSON format are written.
|
||||||
// Optional. Default value os.Stdout.
|
// Optional. Default value os.Stdout.
|
||||||
Output io.Writer
|
Output io.Writer
|
||||||
|
|
||||||
template *fasttemplate.Template
|
template *fasttemplate.Template
|
||||||
colorer *color.Color
|
colorer *color.Color
|
||||||
pool *sync.Pool
|
pool *sync.Pool
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
// DefaultLoggerConfig is the default Logger middleware config.
|
||||||
// DefaultLoggerConfig is the default Logger middleware config.
|
var DefaultLoggerConfig = LoggerConfig{
|
||||||
DefaultLoggerConfig = LoggerConfig{
|
Skipper: DefaultSkipper,
|
||||||
Skipper: DefaultSkipper,
|
Format: `{"time":"${time_rfc3339_nano}","id":"${id}","remote_ip":"${remote_ip}",` +
|
||||||
Format: `{"time":"${time_rfc3339_nano}","id":"${id}","remote_ip":"${remote_ip}",` +
|
`"host":"${host}","method":"${method}","uri":"${uri}","user_agent":"${user_agent}",` +
|
||||||
`"host":"${host}","method":"${method}","uri":"${uri}","user_agent":"${user_agent}",` +
|
`"status":${status},"error":"${error}","latency":${latency},"latency_human":"${latency_human}"` +
|
||||||
`"status":${status},"error":"${error}","latency":${latency},"latency_human":"${latency_human}"` +
|
`,"bytes_in":${bytes_in},"bytes_out":${bytes_out}}` + "\n",
|
||||||
`,"bytes_in":${bytes_in},"bytes_out":${bytes_out}}` + "\n",
|
CustomTimeFormat: "2006-01-02 15:04:05.00000",
|
||||||
CustomTimeFormat: "2006-01-02 15:04:05.00000",
|
colorer: color.New(),
|
||||||
colorer: color.New(),
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Logger returns a middleware that logs HTTP requests.
|
// Logger returns a middleware that logs HTTP requests.
|
||||||
func Logger() echo.MiddlewareFunc {
|
func Logger() echo.MiddlewareFunc {
|
||||||
|
37
vendor/github.com/labstack/echo/v4/middleware/method_override.go
generated
vendored
37
vendor/github.com/labstack/echo/v4/middleware/method_override.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -6,28 +9,24 @@ import (
|
|||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// MethodOverrideConfig defines the config for MethodOverride middleware.
|
||||||
// MethodOverrideConfig defines the config for MethodOverride middleware.
|
type MethodOverrideConfig struct {
|
||||||
MethodOverrideConfig struct {
|
// Skipper defines a function to skip middleware.
|
||||||
// Skipper defines a function to skip middleware.
|
Skipper Skipper
|
||||||
Skipper Skipper
|
|
||||||
|
|
||||||
// Getter is a function that gets overridden method from the request.
|
// Getter is a function that gets overridden method from the request.
|
||||||
// Optional. Default values MethodFromHeader(echo.HeaderXHTTPMethodOverride).
|
// Optional. Default values MethodFromHeader(echo.HeaderXHTTPMethodOverride).
|
||||||
Getter MethodOverrideGetter
|
Getter MethodOverrideGetter
|
||||||
}
|
}
|
||||||
|
|
||||||
// MethodOverrideGetter is a function that gets overridden method from the request
|
// MethodOverrideGetter is a function that gets overridden method from the request
|
||||||
MethodOverrideGetter func(echo.Context) string
|
type MethodOverrideGetter func(echo.Context) string
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
// DefaultMethodOverrideConfig is the default MethodOverride middleware config.
|
||||||
// DefaultMethodOverrideConfig is the default MethodOverride middleware config.
|
var DefaultMethodOverrideConfig = MethodOverrideConfig{
|
||||||
DefaultMethodOverrideConfig = MethodOverrideConfig{
|
Skipper: DefaultSkipper,
|
||||||
Skipper: DefaultSkipper,
|
Getter: MethodFromHeader(echo.HeaderXHTTPMethodOverride),
|
||||||
Getter: MethodFromHeader(echo.HeaderXHTTPMethodOverride),
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// MethodOverride returns a MethodOverride middleware.
|
// MethodOverride returns a MethodOverride middleware.
|
||||||
// MethodOverride middleware checks for the overridden method from the request and
|
// MethodOverride middleware checks for the overridden method from the request and
|
||||||
|
21
vendor/github.com/labstack/echo/v4/middleware/middleware.go
generated
vendored
21
vendor/github.com/labstack/echo/v4/middleware/middleware.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -9,14 +12,12 @@ import (
|
|||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
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.
|
type Skipper func(c 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(c echo.Context)
|
type BeforeFunc func(c echo.Context)
|
||||||
)
|
|
||||||
|
|
||||||
func captureTokens(pattern *regexp.Regexp, input string) *strings.Replacer {
|
func captureTokens(pattern *regexp.Regexp, input string) *strings.Replacer {
|
||||||
groups := pattern.FindAllStringSubmatch(input, -1)
|
groups := pattern.FindAllStringSubmatch(input, -1)
|
||||||
@ -38,9 +39,9 @@ func rewriteRulesRegex(rewrite map[string]string) map[*regexp.Regexp]string {
|
|||||||
rulesRegex := map[*regexp.Regexp]string{}
|
rulesRegex := map[*regexp.Regexp]string{}
|
||||||
for k, v := range rewrite {
|
for k, v := range rewrite {
|
||||||
k = regexp.QuoteMeta(k)
|
k = regexp.QuoteMeta(k)
|
||||||
k = strings.Replace(k, `\*`, "(.*?)", -1)
|
k = strings.ReplaceAll(k, `\*`, "(.*?)")
|
||||||
if strings.HasPrefix(k, `\^`) {
|
if strings.HasPrefix(k, `\^`) {
|
||||||
k = strings.Replace(k, `\^`, "^", -1)
|
k = strings.ReplaceAll(k, `\^`, "^")
|
||||||
}
|
}
|
||||||
k = k + "$"
|
k = k + "$"
|
||||||
rulesRegex[regexp.MustCompile(k)] = v
|
rulesRegex[regexp.MustCompile(k)] = v
|
||||||
@ -53,7 +54,7 @@ func rewriteURL(rewriteRegex map[*regexp.Regexp]string, req *http.Request) error
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Depending how HTTP request is sent RequestURI could contain Scheme://Host/path or be just /path.
|
// Depending on how HTTP request is sent RequestURI could contain Scheme://Host/path or be just /path.
|
||||||
// We only want to use path part for rewriting and therefore trim prefix if it exists
|
// We only want to use path part for rewriting and therefore trim prefix if it exists
|
||||||
rawURI := req.RequestURI
|
rawURI := req.RequestURI
|
||||||
if rawURI != "" && rawURI[0] != '/' {
|
if rawURI != "" && rawURI[0] != '/' {
|
||||||
|
343
vendor/github.com/labstack/echo/v4/middleware/proxy.go
generated
vendored
343
vendor/github.com/labstack/echo/v4/middleware/proxy.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -12,7 +15,6 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
@ -20,101 +22,126 @@ import (
|
|||||||
|
|
||||||
// TODO: Handle TLS proxy
|
// TODO: Handle TLS proxy
|
||||||
|
|
||||||
type (
|
// ProxyConfig defines the config for Proxy middleware.
|
||||||
// ProxyConfig defines the config for Proxy middleware.
|
type ProxyConfig struct {
|
||||||
ProxyConfig struct {
|
// Skipper defines a function to skip middleware.
|
||||||
// Skipper defines a function to skip middleware.
|
Skipper Skipper
|
||||||
Skipper Skipper
|
|
||||||
|
|
||||||
// Balancer defines a load balancing technique.
|
// Balancer defines a load balancing technique.
|
||||||
// Required.
|
// Required.
|
||||||
Balancer ProxyBalancer
|
Balancer ProxyBalancer
|
||||||
|
|
||||||
// Rewrite defines URL path rewrite rules. The values captured in asterisk can be
|
// RetryCount defines the number of times a failed proxied request should be retried
|
||||||
// retrieved by index e.g. $1, $2 and so on.
|
// using the next available ProxyTarget. Defaults to 0, meaning requests are never retried.
|
||||||
// Examples:
|
RetryCount int
|
||||||
// "/old": "/new",
|
|
||||||
// "/api/*": "/$1",
|
|
||||||
// "/js/*": "/public/javascripts/$1",
|
|
||||||
// "/users/*/orders/*": "/user/$1/order/$2",
|
|
||||||
Rewrite map[string]string
|
|
||||||
|
|
||||||
// RegexRewrite defines rewrite rules using regexp.Rexexp with captures
|
// RetryFilter defines a function used to determine if a failed request to a
|
||||||
// Every capture group in the values can be retrieved by index e.g. $1, $2 and so on.
|
// ProxyTarget should be retried. The RetryFilter will only be called when the number
|
||||||
// Example:
|
// of previous retries is less than RetryCount. If the function returns true, the
|
||||||
// "^/old/[0.9]+/": "/new",
|
// request will be retried. The provided error indicates the reason for the request
|
||||||
// "^/api/.+?/(.*)": "/v2/$1",
|
// failure. When the ProxyTarget is unavailable, the error will be an instance of
|
||||||
RegexRewrite map[*regexp.Regexp]string
|
// echo.HTTPError with a Code of http.StatusBadGateway. In all other cases, the error
|
||||||
|
// will indicate an internal error in the Proxy middleware. When a RetryFilter is not
|
||||||
|
// specified, all requests that fail with http.StatusBadGateway will be retried. A custom
|
||||||
|
// RetryFilter can be provided to only retry specific requests. Note that RetryFilter is
|
||||||
|
// only called when the request to the target fails, or an internal error in the Proxy
|
||||||
|
// middleware has occurred. Successful requests that return a non-200 response code cannot
|
||||||
|
// be retried.
|
||||||
|
RetryFilter func(c echo.Context, e error) bool
|
||||||
|
|
||||||
// Context key to store selected ProxyTarget into context.
|
// ErrorHandler defines a function which can be used to return custom errors from
|
||||||
// Optional. Default value "target".
|
// the Proxy middleware. ErrorHandler is only invoked when there has been
|
||||||
ContextKey string
|
// either an internal error in the Proxy middleware or the ProxyTarget is
|
||||||
|
// unavailable. Due to the way requests are proxied, ErrorHandler is not invoked
|
||||||
|
// when a ProxyTarget returns a non-200 response. In these cases, the response
|
||||||
|
// is already written so errors cannot be modified. ErrorHandler is only
|
||||||
|
// invoked after all retry attempts have been exhausted.
|
||||||
|
ErrorHandler func(c echo.Context, err error) error
|
||||||
|
|
||||||
// To customize the transport to remote.
|
// Rewrite defines URL path rewrite rules. The values captured in asterisk can be
|
||||||
// Examples: If custom TLS certificates are required.
|
// retrieved by index e.g. $1, $2 and so on.
|
||||||
Transport http.RoundTripper
|
// Examples:
|
||||||
|
// "/old": "/new",
|
||||||
|
// "/api/*": "/$1",
|
||||||
|
// "/js/*": "/public/javascripts/$1",
|
||||||
|
// "/users/*/orders/*": "/user/$1/order/$2",
|
||||||
|
Rewrite map[string]string
|
||||||
|
|
||||||
// ModifyResponse defines function to modify response from ProxyTarget.
|
// RegexRewrite defines rewrite rules using regexp.Rexexp with captures
|
||||||
ModifyResponse func(*http.Response) error
|
// Every capture group in the values can be retrieved by index e.g. $1, $2 and so on.
|
||||||
}
|
// Example:
|
||||||
|
// "^/old/[0.9]+/": "/new",
|
||||||
|
// "^/api/.+?/(.*)": "/v2/$1",
|
||||||
|
RegexRewrite map[*regexp.Regexp]string
|
||||||
|
|
||||||
// ProxyTarget defines the upstream target.
|
// Context key to store selected ProxyTarget into context.
|
||||||
ProxyTarget struct {
|
// Optional. Default value "target".
|
||||||
Name string
|
ContextKey string
|
||||||
URL *url.URL
|
|
||||||
Meta echo.Map
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProxyBalancer defines an interface to implement a load balancing technique.
|
// To customize the transport to remote.
|
||||||
ProxyBalancer interface {
|
// Examples: If custom TLS certificates are required.
|
||||||
AddTarget(*ProxyTarget) bool
|
Transport http.RoundTripper
|
||||||
RemoveTarget(string) bool
|
|
||||||
Next(echo.Context) *ProxyTarget
|
|
||||||
}
|
|
||||||
|
|
||||||
// TargetProvider defines an interface that gives the opportunity for balancer to return custom errors when selecting target.
|
// ModifyResponse defines function to modify response from ProxyTarget.
|
||||||
TargetProvider interface {
|
ModifyResponse func(*http.Response) error
|
||||||
NextTarget(echo.Context) (*ProxyTarget, error)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
commonBalancer struct {
|
// ProxyTarget defines the upstream target.
|
||||||
targets []*ProxyTarget
|
type ProxyTarget struct {
|
||||||
mutex sync.RWMutex
|
Name string
|
||||||
}
|
URL *url.URL
|
||||||
|
Meta echo.Map
|
||||||
|
}
|
||||||
|
|
||||||
// RandomBalancer implements a random load balancing technique.
|
// ProxyBalancer defines an interface to implement a load balancing technique.
|
||||||
randomBalancer struct {
|
type ProxyBalancer interface {
|
||||||
*commonBalancer
|
AddTarget(*ProxyTarget) bool
|
||||||
random *rand.Rand
|
RemoveTarget(string) bool
|
||||||
}
|
Next(echo.Context) *ProxyTarget
|
||||||
|
}
|
||||||
|
|
||||||
// RoundRobinBalancer implements a round-robin load balancing technique.
|
// TargetProvider defines an interface that gives the opportunity for balancer
|
||||||
roundRobinBalancer struct {
|
// to return custom errors when selecting target.
|
||||||
*commonBalancer
|
type TargetProvider interface {
|
||||||
i uint32
|
NextTarget(echo.Context) (*ProxyTarget, error)
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
type commonBalancer struct {
|
||||||
// DefaultProxyConfig is the default Proxy middleware config.
|
targets []*ProxyTarget
|
||||||
DefaultProxyConfig = ProxyConfig{
|
mutex sync.Mutex
|
||||||
Skipper: DefaultSkipper,
|
}
|
||||||
ContextKey: "target",
|
|
||||||
}
|
// RandomBalancer implements a random load balancing technique.
|
||||||
)
|
type randomBalancer struct {
|
||||||
|
commonBalancer
|
||||||
|
random *rand.Rand
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoundRobinBalancer implements a round-robin load balancing technique.
|
||||||
|
type roundRobinBalancer struct {
|
||||||
|
commonBalancer
|
||||||
|
// tracking the index on `targets` slice for the next `*ProxyTarget` to be used
|
||||||
|
i int
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultProxyConfig is the default Proxy middleware config.
|
||||||
|
var DefaultProxyConfig = ProxyConfig{
|
||||||
|
Skipper: DefaultSkipper,
|
||||||
|
ContextKey: "target",
|
||||||
|
}
|
||||||
|
|
||||||
func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler {
|
func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
in, _, err := c.Response().Hijack()
|
in, _, err := c.Response().Hijack()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Set("_error", fmt.Sprintf("proxy raw, hijack error=%v, url=%s", t.URL, err))
|
c.Set("_error", fmt.Errorf("proxy raw, hijack error=%w, url=%s", err, t.URL))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer in.Close()
|
defer in.Close()
|
||||||
|
|
||||||
out, err := net.Dial("tcp", t.URL.Host)
|
out, err := net.Dial("tcp", t.URL.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Set("_error", echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, dial error=%v, url=%s", t.URL, err)))
|
c.Set("_error", echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, dial error=%v, url=%s", err, t.URL)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer out.Close()
|
defer out.Close()
|
||||||
@ -122,7 +149,7 @@ func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler {
|
|||||||
// Write header
|
// Write header
|
||||||
err = r.Write(out)
|
err = r.Write(out)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Set("_error", echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, request header copy error=%v, url=%s", t.URL, err)))
|
c.Set("_error", echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("proxy raw, request header copy error=%v, url=%s", err, t.URL)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,39 +163,44 @@ func proxyRaw(t *ProxyTarget, c echo.Context) http.Handler {
|
|||||||
go cp(in, out)
|
go cp(in, out)
|
||||||
err = <-errCh
|
err = <-errCh
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
c.Set("_error", fmt.Errorf("proxy raw, copy body error=%v, url=%s", t.URL, err))
|
c.Set("_error", fmt.Errorf("proxy raw, copy body error=%w, url=%s", err, t.URL))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRandomBalancer returns a random proxy balancer.
|
// NewRandomBalancer returns a random proxy balancer.
|
||||||
func NewRandomBalancer(targets []*ProxyTarget) ProxyBalancer {
|
func NewRandomBalancer(targets []*ProxyTarget) ProxyBalancer {
|
||||||
b := &randomBalancer{commonBalancer: new(commonBalancer)}
|
b := randomBalancer{}
|
||||||
b.targets = targets
|
b.targets = targets
|
||||||
return b
|
b.random = rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
|
||||||
|
return &b
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRoundRobinBalancer returns a round-robin proxy balancer.
|
// NewRoundRobinBalancer returns a round-robin proxy balancer.
|
||||||
func NewRoundRobinBalancer(targets []*ProxyTarget) ProxyBalancer {
|
func NewRoundRobinBalancer(targets []*ProxyTarget) ProxyBalancer {
|
||||||
b := &roundRobinBalancer{commonBalancer: new(commonBalancer)}
|
b := roundRobinBalancer{}
|
||||||
b.targets = targets
|
b.targets = targets
|
||||||
return b
|
return &b
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTarget adds an upstream target to the list.
|
// AddTarget adds an upstream target to the list and returns `true`.
|
||||||
|
//
|
||||||
|
// However, if a target with the same name already exists then the operation is aborted returning `false`.
|
||||||
func (b *commonBalancer) AddTarget(target *ProxyTarget) bool {
|
func (b *commonBalancer) AddTarget(target *ProxyTarget) bool {
|
||||||
|
b.mutex.Lock()
|
||||||
|
defer b.mutex.Unlock()
|
||||||
for _, t := range b.targets {
|
for _, t := range b.targets {
|
||||||
if t.Name == target.Name {
|
if t.Name == target.Name {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b.mutex.Lock()
|
|
||||||
defer b.mutex.Unlock()
|
|
||||||
b.targets = append(b.targets, target)
|
b.targets = append(b.targets, target)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveTarget removes an upstream target from the list.
|
// RemoveTarget removes an upstream target from the list by name.
|
||||||
|
//
|
||||||
|
// Returns `true` on success, `false` if no target with the name is found.
|
||||||
func (b *commonBalancer) RemoveTarget(name string) bool {
|
func (b *commonBalancer) RemoveTarget(name string) bool {
|
||||||
b.mutex.Lock()
|
b.mutex.Lock()
|
||||||
defer b.mutex.Unlock()
|
defer b.mutex.Unlock()
|
||||||
@ -182,21 +214,58 @@ func (b *commonBalancer) RemoveTarget(name string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Next randomly returns an upstream target.
|
// Next randomly returns an upstream target.
|
||||||
|
//
|
||||||
|
// Note: `nil` is returned in case upstream target list is empty.
|
||||||
func (b *randomBalancer) Next(c echo.Context) *ProxyTarget {
|
func (b *randomBalancer) Next(c echo.Context) *ProxyTarget {
|
||||||
if b.random == nil {
|
b.mutex.Lock()
|
||||||
b.random = rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
|
defer b.mutex.Unlock()
|
||||||
|
if len(b.targets) == 0 {
|
||||||
|
return nil
|
||||||
|
} else if len(b.targets) == 1 {
|
||||||
|
return b.targets[0]
|
||||||
}
|
}
|
||||||
b.mutex.RLock()
|
|
||||||
defer b.mutex.RUnlock()
|
|
||||||
return b.targets[b.random.Intn(len(b.targets))]
|
return b.targets[b.random.Intn(len(b.targets))]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next returns an upstream target using round-robin technique.
|
// Next returns an upstream target using round-robin technique. In the case
|
||||||
|
// where a previously failed request is being retried, the round-robin
|
||||||
|
// balancer will attempt to use the next target relative to the original
|
||||||
|
// request. If the list of targets held by the balancer is modified while a
|
||||||
|
// failed request is being retried, it is possible that the balancer will
|
||||||
|
// return the original failed target.
|
||||||
|
//
|
||||||
|
// Note: `nil` is returned in case upstream target list is empty.
|
||||||
func (b *roundRobinBalancer) Next(c echo.Context) *ProxyTarget {
|
func (b *roundRobinBalancer) Next(c echo.Context) *ProxyTarget {
|
||||||
b.i = b.i % uint32(len(b.targets))
|
b.mutex.Lock()
|
||||||
t := b.targets[b.i]
|
defer b.mutex.Unlock()
|
||||||
atomic.AddUint32(&b.i, 1)
|
if len(b.targets) == 0 {
|
||||||
return t
|
return nil
|
||||||
|
} else if len(b.targets) == 1 {
|
||||||
|
return b.targets[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
var i int
|
||||||
|
const lastIdxKey = "_round_robin_last_index"
|
||||||
|
// This request is a retry, start from the index of the previous
|
||||||
|
// target to ensure we don't attempt to retry the request with
|
||||||
|
// the same failed target
|
||||||
|
if c.Get(lastIdxKey) != nil {
|
||||||
|
i = c.Get(lastIdxKey).(int)
|
||||||
|
i++
|
||||||
|
if i >= len(b.targets) {
|
||||||
|
i = 0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// This is a first time request, use the global index
|
||||||
|
if b.i >= len(b.targets) {
|
||||||
|
b.i = 0
|
||||||
|
}
|
||||||
|
i = b.i
|
||||||
|
b.i++
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Set(lastIdxKey, i)
|
||||||
|
return b.targets[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Proxy returns a Proxy middleware.
|
// Proxy returns a Proxy middleware.
|
||||||
@ -211,14 +280,26 @@ func Proxy(balancer ProxyBalancer) echo.MiddlewareFunc {
|
|||||||
// ProxyWithConfig returns a Proxy middleware with config.
|
// ProxyWithConfig returns a Proxy middleware with config.
|
||||||
// See: `Proxy()`
|
// See: `Proxy()`
|
||||||
func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc {
|
func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc {
|
||||||
|
if config.Balancer == nil {
|
||||||
|
panic("echo: proxy middleware requires balancer")
|
||||||
|
}
|
||||||
// Defaults
|
// Defaults
|
||||||
if config.Skipper == nil {
|
if config.Skipper == nil {
|
||||||
config.Skipper = DefaultProxyConfig.Skipper
|
config.Skipper = DefaultProxyConfig.Skipper
|
||||||
}
|
}
|
||||||
if config.Balancer == nil {
|
if config.RetryFilter == nil {
|
||||||
panic("echo: proxy middleware requires balancer")
|
config.RetryFilter = func(c echo.Context, e error) bool {
|
||||||
|
if httpErr, ok := e.(*echo.HTTPError); ok {
|
||||||
|
return httpErr.Code == http.StatusBadGateway
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if config.ErrorHandler == nil {
|
||||||
|
config.ErrorHandler = func(c echo.Context, err error) error {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Rewrite != nil {
|
if config.Rewrite != nil {
|
||||||
if config.RegexRewrite == nil {
|
if config.RegexRewrite == nil {
|
||||||
config.RegexRewrite = make(map[*regexp.Regexp]string)
|
config.RegexRewrite = make(map[*regexp.Regexp]string)
|
||||||
@ -229,28 +310,17 @@ func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
provider, isTargetProvider := config.Balancer.(TargetProvider)
|
provider, isTargetProvider := config.Balancer.(TargetProvider)
|
||||||
|
|
||||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
return func(c echo.Context) (err error) {
|
return func(c echo.Context) error {
|
||||||
if config.Skipper(c) {
|
if config.Skipper(c) {
|
||||||
return next(c)
|
return next(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
req := c.Request()
|
req := c.Request()
|
||||||
res := c.Response()
|
res := c.Response()
|
||||||
|
|
||||||
var tgt *ProxyTarget
|
|
||||||
if isTargetProvider {
|
|
||||||
tgt, err = provider.NextTarget(c)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tgt = config.Balancer.Next(c)
|
|
||||||
}
|
|
||||||
c.Set(config.ContextKey, tgt)
|
|
||||||
|
|
||||||
if err := rewriteURL(config.RegexRewrite, req); err != nil {
|
if err := rewriteURL(config.RegexRewrite, req); err != nil {
|
||||||
return err
|
return config.ErrorHandler(c, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix header
|
// Fix header
|
||||||
@ -266,19 +336,52 @@ func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc {
|
|||||||
req.Header.Set(echo.HeaderXForwardedFor, c.RealIP())
|
req.Header.Set(echo.HeaderXForwardedFor, c.RealIP())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Proxy
|
retries := config.RetryCount
|
||||||
switch {
|
for {
|
||||||
case c.IsWebSocket():
|
var tgt *ProxyTarget
|
||||||
proxyRaw(tgt, c).ServeHTTP(res, req)
|
var err error
|
||||||
case req.Header.Get(echo.HeaderAccept) == "text/event-stream":
|
if isTargetProvider {
|
||||||
default:
|
tgt, err = provider.NextTarget(c)
|
||||||
proxyHTTP(tgt, c, config).ServeHTTP(res, req)
|
if err != nil {
|
||||||
}
|
return config.ErrorHandler(c, err)
|
||||||
if e, ok := c.Get("_error").(error); ok {
|
}
|
||||||
err = e
|
} else {
|
||||||
}
|
tgt = config.Balancer.Next(c)
|
||||||
|
}
|
||||||
|
|
||||||
return
|
c.Set(config.ContextKey, tgt)
|
||||||
|
|
||||||
|
//If retrying a failed request, clear any previous errors from
|
||||||
|
//context here so that balancers have the option to check for
|
||||||
|
//errors that occurred using previous target
|
||||||
|
if retries < config.RetryCount {
|
||||||
|
c.Set("_error", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is needed for ProxyConfig.ModifyResponse and/or ProxyConfig.Transport to be able to process the Request
|
||||||
|
// that Balancer may have replaced with c.SetRequest.
|
||||||
|
req = c.Request()
|
||||||
|
|
||||||
|
// Proxy
|
||||||
|
switch {
|
||||||
|
case c.IsWebSocket():
|
||||||
|
proxyRaw(tgt, c).ServeHTTP(res, req)
|
||||||
|
default: // even SSE requests
|
||||||
|
proxyHTTP(tgt, c, config).ServeHTTP(res, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
err, hasError := c.Get("_error").(error)
|
||||||
|
if !hasError {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
retry := retries > 0 && config.RetryFilter(c, err)
|
||||||
|
if !retry {
|
||||||
|
return config.ErrorHandler(c, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
retries--
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
112
vendor/github.com/labstack/echo/v4/middleware/rate_limiter.go
generated
vendored
112
vendor/github.com/labstack/echo/v4/middleware/rate_limiter.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -9,39 +12,34 @@ import (
|
|||||||
"golang.org/x/time/rate"
|
"golang.org/x/time/rate"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// RateLimiterStore is the interface to be implemented by custom stores.
|
||||||
// RateLimiterStore is the interface to be implemented by custom stores.
|
type RateLimiterStore interface {
|
||||||
RateLimiterStore interface {
|
// Stores for the rate limiter have to implement the Allow method
|
||||||
// Stores for the rate limiter have to implement the Allow method
|
Allow(identifier string) (bool, error)
|
||||||
Allow(identifier string) (bool, error)
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
// RateLimiterConfig defines the configuration for the rate limiter
|
||||||
// RateLimiterConfig defines the configuration for the rate limiter
|
type RateLimiterConfig struct {
|
||||||
RateLimiterConfig struct {
|
Skipper Skipper
|
||||||
Skipper Skipper
|
BeforeFunc BeforeFunc
|
||||||
BeforeFunc BeforeFunc
|
// IdentifierExtractor uses echo.Context to extract the identifier for a visitor
|
||||||
// IdentifierExtractor uses echo.Context to extract the identifier for a visitor
|
IdentifierExtractor Extractor
|
||||||
IdentifierExtractor Extractor
|
// Store defines a store for the rate limiter
|
||||||
// Store defines a store for the rate limiter
|
Store RateLimiterStore
|
||||||
Store RateLimiterStore
|
// ErrorHandler provides a handler to be called when IdentifierExtractor returns an error
|
||||||
// ErrorHandler provides a handler to be called when IdentifierExtractor returns an error
|
ErrorHandler func(context echo.Context, err error) error
|
||||||
ErrorHandler func(context echo.Context, err error) error
|
// DenyHandler provides a handler to be called when RateLimiter denies access
|
||||||
// DenyHandler provides a handler to be called when RateLimiter denies access
|
DenyHandler func(context echo.Context, identifier string, err error) error
|
||||||
DenyHandler func(context echo.Context, identifier string, err error) error
|
}
|
||||||
}
|
|
||||||
// Extractor is used to extract data from echo.Context
|
|
||||||
Extractor func(context echo.Context) (string, error)
|
|
||||||
)
|
|
||||||
|
|
||||||
// errors
|
// Extractor is used to extract data from echo.Context
|
||||||
var (
|
type Extractor func(context echo.Context) (string, error)
|
||||||
// ErrRateLimitExceeded denotes an error raised when rate limit is exceeded
|
|
||||||
ErrRateLimitExceeded = echo.NewHTTPError(http.StatusTooManyRequests, "rate limit exceeded")
|
// ErrRateLimitExceeded denotes an error raised when rate limit is exceeded
|
||||||
// ErrExtractorError denotes an error raised when extractor function is unsuccessful
|
var ErrRateLimitExceeded = echo.NewHTTPError(http.StatusTooManyRequests, "rate limit exceeded")
|
||||||
ErrExtractorError = echo.NewHTTPError(http.StatusForbidden, "error while extracting identifier")
|
|
||||||
)
|
// ErrExtractorError denotes an error raised when extractor function is unsuccessful
|
||||||
|
var ErrExtractorError = echo.NewHTTPError(http.StatusForbidden, "error while extracting identifier")
|
||||||
|
|
||||||
// DefaultRateLimiterConfig defines default values for RateLimiterConfig
|
// DefaultRateLimiterConfig defines default values for RateLimiterConfig
|
||||||
var DefaultRateLimiterConfig = RateLimiterConfig{
|
var DefaultRateLimiterConfig = RateLimiterConfig{
|
||||||
@ -150,23 +148,24 @@ func RateLimiterWithConfig(config RateLimiterConfig) echo.MiddlewareFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type (
|
// RateLimiterMemoryStore is the built-in store implementation for RateLimiter
|
||||||
// RateLimiterMemoryStore is the built-in store implementation for RateLimiter
|
type RateLimiterMemoryStore struct {
|
||||||
RateLimiterMemoryStore struct {
|
visitors map[string]*Visitor
|
||||||
visitors map[string]*Visitor
|
mutex sync.Mutex
|
||||||
mutex sync.Mutex
|
rate rate.Limit // for more info check out Limiter docs - https://pkg.go.dev/golang.org/x/time/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
|
||||||
}
|
|
||||||
// Visitor signifies a unique user's limiter details
|
timeNow func() time.Time
|
||||||
Visitor struct {
|
}
|
||||||
*rate.Limiter
|
|
||||||
lastSeen time.Time
|
// Visitor signifies a unique user's limiter details
|
||||||
}
|
type Visitor struct {
|
||||||
)
|
*rate.Limiter
|
||||||
|
lastSeen time.Time
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
NewRateLimiterMemoryStore returns an instance of RateLimiterMemoryStore with
|
NewRateLimiterMemoryStore returns an instance of RateLimiterMemoryStore with
|
||||||
@ -219,7 +218,8 @@ func NewRateLimiterMemoryStoreWithConfig(config RateLimiterMemoryStoreConfig) (s
|
|||||||
store.burst = int(config.Rate)
|
store.burst = int(config.Rate)
|
||||||
}
|
}
|
||||||
store.visitors = make(map[string]*Visitor)
|
store.visitors = make(map[string]*Visitor)
|
||||||
store.lastCleanup = now()
|
store.timeNow = time.Now
|
||||||
|
store.lastCleanup = store.timeNow()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,12 +244,13 @@ func (store *RateLimiterMemoryStore) Allow(identifier string) (bool, error) {
|
|||||||
limiter.Limiter = rate.NewLimiter(store.rate, store.burst)
|
limiter.Limiter = rate.NewLimiter(store.rate, store.burst)
|
||||||
store.visitors[identifier] = limiter
|
store.visitors[identifier] = limiter
|
||||||
}
|
}
|
||||||
limiter.lastSeen = now()
|
now := store.timeNow()
|
||||||
if now().Sub(store.lastCleanup) > store.expiresIn {
|
limiter.lastSeen = now
|
||||||
|
if now.Sub(store.lastCleanup) > store.expiresIn {
|
||||||
store.cleanupStaleVisitors()
|
store.cleanupStaleVisitors()
|
||||||
}
|
}
|
||||||
store.mutex.Unlock()
|
store.mutex.Unlock()
|
||||||
return limiter.AllowN(now(), 1), nil
|
return limiter.AllowN(store.timeNow(), 1), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -258,14 +259,9 @@ of users who haven't visited again after the configured expiry time has elapsed
|
|||||||
*/
|
*/
|
||||||
func (store *RateLimiterMemoryStore) cleanupStaleVisitors() {
|
func (store *RateLimiterMemoryStore) cleanupStaleVisitors() {
|
||||||
for id, visitor := range store.visitors {
|
for id, visitor := range store.visitors {
|
||||||
if now().Sub(visitor.lastSeen) > store.expiresIn {
|
if store.timeNow().Sub(visitor.lastSeen) > store.expiresIn {
|
||||||
delete(store.visitors, id)
|
delete(store.visitors, id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
store.lastCleanup = now()
|
store.lastCleanup = store.timeNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
actual time method which is mocked in test file
|
|
||||||
*/
|
|
||||||
var now = time.Now
|
|
||||||
|
95
vendor/github.com/labstack/echo/v4/middleware/recover.go
generated
vendored
95
vendor/github.com/labstack/echo/v4/middleware/recover.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -9,49 +12,52 @@ import (
|
|||||||
"github.com/labstack/gommon/log"
|
"github.com/labstack/gommon/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// LogErrorFunc defines a function for custom logging in the middleware.
|
||||||
|
type LogErrorFunc func(c echo.Context, err error, stack []byte) error
|
||||||
|
|
||||||
|
// RecoverConfig defines the config for Recover middleware.
|
||||||
|
type RecoverConfig struct {
|
||||||
|
// Skipper defines a function to skip middleware.
|
||||||
|
Skipper Skipper
|
||||||
|
|
||||||
|
// Size of the stack to be printed.
|
||||||
|
// Optional. Default value 4KB.
|
||||||
|
StackSize int `yaml:"stack_size"`
|
||||||
|
|
||||||
|
// DisableStackAll disables formatting stack traces of all other goroutines
|
||||||
|
// into buffer after the trace for the current goroutine.
|
||||||
|
// Optional. Default value false.
|
||||||
|
DisableStackAll bool `yaml:"disable_stack_all"`
|
||||||
|
|
||||||
|
// DisablePrintStack disables printing stack trace.
|
||||||
|
// Optional. Default value as false.
|
||||||
|
DisablePrintStack bool `yaml:"disable_print_stack"`
|
||||||
|
|
||||||
|
// LogLevel is log level to printing stack trace.
|
||||||
|
// Optional. Default value 0 (Print).
|
||||||
|
LogLevel log.Lvl
|
||||||
|
|
||||||
// LogErrorFunc defines a function for custom logging in the middleware.
|
// LogErrorFunc defines a function for custom logging in the middleware.
|
||||||
LogErrorFunc func(c echo.Context, err error, stack []byte) error
|
// If it's set you don't need to provide LogLevel for config.
|
||||||
|
// If this function returns nil, the centralized HTTPErrorHandler will not be called.
|
||||||
|
LogErrorFunc LogErrorFunc
|
||||||
|
|
||||||
// RecoverConfig defines the config for Recover middleware.
|
// DisableErrorHandler disables the call to centralized HTTPErrorHandler.
|
||||||
RecoverConfig struct {
|
// The recovered error is then passed back to upstream middleware, instead of swallowing the error.
|
||||||
// Skipper defines a function to skip middleware.
|
// Optional. Default value false.
|
||||||
Skipper Skipper
|
DisableErrorHandler bool `yaml:"disable_error_handler"`
|
||||||
|
}
|
||||||
|
|
||||||
// Size of the stack to be printed.
|
// DefaultRecoverConfig is the default Recover middleware config.
|
||||||
// Optional. Default value 4KB.
|
var DefaultRecoverConfig = RecoverConfig{
|
||||||
StackSize int `yaml:"stack_size"`
|
Skipper: DefaultSkipper,
|
||||||
|
StackSize: 4 << 10, // 4 KB
|
||||||
// DisableStackAll disables formatting stack traces of all other goroutines
|
DisableStackAll: false,
|
||||||
// into buffer after the trace for the current goroutine.
|
DisablePrintStack: false,
|
||||||
// Optional. Default value false.
|
LogLevel: 0,
|
||||||
DisableStackAll bool `yaml:"disable_stack_all"`
|
LogErrorFunc: nil,
|
||||||
|
DisableErrorHandler: false,
|
||||||
// DisablePrintStack disables printing stack trace.
|
}
|
||||||
// Optional. Default value as false.
|
|
||||||
DisablePrintStack bool `yaml:"disable_print_stack"`
|
|
||||||
|
|
||||||
// LogLevel is log level to printing stack trace.
|
|
||||||
// Optional. Default value 0 (Print).
|
|
||||||
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
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// DefaultRecoverConfig is the default Recover middleware config.
|
|
||||||
DefaultRecoverConfig = RecoverConfig{
|
|
||||||
Skipper: DefaultSkipper,
|
|
||||||
StackSize: 4 << 10, // 4 KB
|
|
||||||
DisableStackAll: false,
|
|
||||||
DisablePrintStack: false,
|
|
||||||
LogLevel: 0,
|
|
||||||
LogErrorFunc: nil,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Recover returns a middleware which recovers from panics anywhere in the chain
|
// Recover returns a middleware which recovers from panics anywhere in the chain
|
||||||
// and handles the control to the centralized HTTPErrorHandler.
|
// and handles the control to the centralized HTTPErrorHandler.
|
||||||
@ -71,7 +77,7 @@ func RecoverWithConfig(config RecoverConfig) echo.MiddlewareFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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) (returnErr error) {
|
||||||
if config.Skipper(c) {
|
if config.Skipper(c) {
|
||||||
return next(c)
|
return next(c)
|
||||||
}
|
}
|
||||||
@ -113,7 +119,12 @@ func RecoverWithConfig(config RecoverConfig) echo.MiddlewareFunc {
|
|||||||
c.Logger().Print(msg)
|
c.Logger().Print(msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.Error(err)
|
|
||||||
|
if err != nil && !config.DisableErrorHandler {
|
||||||
|
c.Error(err)
|
||||||
|
} else {
|
||||||
|
returnErr = err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return next(c)
|
return next(c)
|
||||||
|
3
vendor/github.com/labstack/echo/v4/middleware/redirect.go
generated
vendored
3
vendor/github.com/labstack/echo/v4/middleware/redirect.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
46
vendor/github.com/labstack/echo/v4/middleware/request_id.go
generated
vendored
46
vendor/github.com/labstack/echo/v4/middleware/request_id.go
generated
vendored
@ -1,36 +1,34 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"github.com/labstack/gommon/random"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// RequestIDConfig defines the config for RequestID middleware.
|
||||||
// RequestIDConfig defines the config for RequestID middleware.
|
type RequestIDConfig struct {
|
||||||
RequestIDConfig struct {
|
// Skipper defines a function to skip middleware.
|
||||||
// Skipper defines a function to skip middleware.
|
Skipper Skipper
|
||||||
Skipper Skipper
|
|
||||||
|
|
||||||
// Generator defines a function to generate an ID.
|
// Generator defines a function to generate an ID.
|
||||||
// Optional. Default value random.String(32).
|
// Optional. Defaults to generator for random string of length 32.
|
||||||
Generator func() string
|
Generator func() string
|
||||||
|
|
||||||
// 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 defines what header to look for to populate the id
|
||||||
TargetHeader string
|
TargetHeader string
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
// DefaultRequestIDConfig is the default RequestID middleware config.
|
||||||
// DefaultRequestIDConfig is the default RequestID middleware config.
|
var DefaultRequestIDConfig = RequestIDConfig{
|
||||||
DefaultRequestIDConfig = RequestIDConfig{
|
Skipper: DefaultSkipper,
|
||||||
Skipper: DefaultSkipper,
|
Generator: generator,
|
||||||
Generator: generator,
|
TargetHeader: echo.HeaderXRequestID,
|
||||||
TargetHeader: echo.HeaderXRequestID,
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// RequestID returns a X-Request-ID middleware.
|
// RequestID returns a X-Request-ID middleware.
|
||||||
func RequestID() echo.MiddlewareFunc {
|
func RequestID() echo.MiddlewareFunc {
|
||||||
@ -73,5 +71,5 @@ func RequestIDWithConfig(config RequestIDConfig) echo.MiddlewareFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func generator() string {
|
func generator() string {
|
||||||
return random.String(32)
|
return randomString(32)
|
||||||
}
|
}
|
||||||
|
31
vendor/github.com/labstack/echo/v4/middleware/request_logger.go
generated
vendored
31
vendor/github.com/labstack/echo/v4/middleware/request_logger.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -8,6 +11,30 @@ import (
|
|||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Example for `slog` https://pkg.go.dev/log/slog
|
||||||
|
// logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
|
||||||
|
// e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{
|
||||||
|
// LogStatus: true,
|
||||||
|
// LogURI: true,
|
||||||
|
// LogError: true,
|
||||||
|
// HandleError: true, // forwards error to the global error handler, so it can decide appropriate status code
|
||||||
|
// LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error {
|
||||||
|
// if v.Error == nil {
|
||||||
|
// logger.LogAttrs(context.Background(), slog.LevelInfo, "REQUEST",
|
||||||
|
// slog.String("uri", v.URI),
|
||||||
|
// slog.Int("status", v.Status),
|
||||||
|
// )
|
||||||
|
// } else {
|
||||||
|
// logger.LogAttrs(context.Background(), slog.LevelError, "REQUEST_ERROR",
|
||||||
|
// slog.String("uri", v.URI),
|
||||||
|
// slog.Int("status", v.Status),
|
||||||
|
// slog.String("err", v.Error.Error()),
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// return nil
|
||||||
|
// },
|
||||||
|
// }))
|
||||||
|
//
|
||||||
// Example for `fmt.Printf`
|
// Example for `fmt.Printf`
|
||||||
// e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{
|
// e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{
|
||||||
// LogStatus: true,
|
// LogStatus: true,
|
||||||
@ -225,7 +252,7 @@ func (config RequestLoggerConfig) ToMiddleware() (echo.MiddlewareFunc, error) {
|
|||||||
if config.Skipper == nil {
|
if config.Skipper == nil {
|
||||||
config.Skipper = DefaultSkipper
|
config.Skipper = DefaultSkipper
|
||||||
}
|
}
|
||||||
now = time.Now
|
now := time.Now
|
||||||
if config.timeNow != nil {
|
if config.timeNow != nil {
|
||||||
now = config.timeNow
|
now = config.timeNow
|
||||||
}
|
}
|
||||||
@ -257,7 +284,7 @@ func (config RequestLoggerConfig) ToMiddleware() (echo.MiddlewareFunc, error) {
|
|||||||
config.BeforeNextFunc(c)
|
config.BeforeNextFunc(c)
|
||||||
}
|
}
|
||||||
err := next(c)
|
err := next(c)
|
||||||
if config.HandleError {
|
if err != nil && config.HandleError {
|
||||||
c.Error(err)
|
c.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
44
vendor/github.com/labstack/echo/v4/middleware/responsecontroller_1.19.go
generated
vendored
Normal file
44
vendor/github.com/labstack/echo/v4/middleware/responsecontroller_1.19.go
generated
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
|
//go:build !go1.20
|
||||||
|
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: remove when Go 1.23 is released and we do not support 1.19 anymore
|
||||||
|
func responseControllerFlush(rw http.ResponseWriter) error {
|
||||||
|
for {
|
||||||
|
switch t := rw.(type) {
|
||||||
|
case interface{ FlushError() error }:
|
||||||
|
return t.FlushError()
|
||||||
|
case http.Flusher:
|
||||||
|
t.Flush()
|
||||||
|
return nil
|
||||||
|
case interface{ Unwrap() http.ResponseWriter }:
|
||||||
|
rw = t.Unwrap()
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("%w", http.ErrNotSupported)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove when Go 1.23 is released and we do not support 1.19 anymore
|
||||||
|
func responseControllerHijack(rw http.ResponseWriter) (net.Conn, *bufio.ReadWriter, error) {
|
||||||
|
for {
|
||||||
|
switch t := rw.(type) {
|
||||||
|
case http.Hijacker:
|
||||||
|
return t.Hijack()
|
||||||
|
case interface{ Unwrap() http.ResponseWriter }:
|
||||||
|
rw = t.Unwrap()
|
||||||
|
default:
|
||||||
|
return nil, nil, fmt.Errorf("%w", http.ErrNotSupported)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
vendor/github.com/labstack/echo/v4/middleware/responsecontroller_1.20.go
generated
vendored
Normal file
20
vendor/github.com/labstack/echo/v4/middleware/responsecontroller_1.20.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
|
//go:build go1.20
|
||||||
|
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func responseControllerFlush(rw http.ResponseWriter) error {
|
||||||
|
return http.NewResponseController(rw).Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func responseControllerHijack(rw http.ResponseWriter) (net.Conn, *bufio.ReadWriter, error) {
|
||||||
|
return http.NewResponseController(rw).Hijack()
|
||||||
|
}
|
55
vendor/github.com/labstack/echo/v4/middleware/rewrite.go
generated
vendored
55
vendor/github.com/labstack/echo/v4/middleware/rewrite.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -6,37 +9,33 @@ import (
|
|||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// RewriteConfig defines the config for Rewrite middleware.
|
||||||
// RewriteConfig defines the config for Rewrite middleware.
|
type RewriteConfig struct {
|
||||||
RewriteConfig struct {
|
// Skipper defines a function to skip middleware.
|
||||||
// Skipper defines a function to skip middleware.
|
Skipper Skipper
|
||||||
Skipper Skipper
|
|
||||||
|
|
||||||
// Rules defines the URL path rewrite rules. The values captured in asterisk can be
|
// Rules defines the URL path rewrite rules. The values captured in asterisk can be
|
||||||
// retrieved by index e.g. $1, $2 and so on.
|
// retrieved by index e.g. $1, $2 and so on.
|
||||||
// Example:
|
// Example:
|
||||||
// "/old": "/new",
|
// "/old": "/new",
|
||||||
// "/api/*": "/$1",
|
// "/api/*": "/$1",
|
||||||
// "/js/*": "/public/javascripts/$1",
|
// "/js/*": "/public/javascripts/$1",
|
||||||
// "/users/*/orders/*": "/user/$1/order/$2",
|
// "/users/*/orders/*": "/user/$1/order/$2",
|
||||||
// Required.
|
// Required.
|
||||||
Rules map[string]string `yaml:"rules"`
|
Rules map[string]string `yaml:"rules"`
|
||||||
|
|
||||||
// RegexRules defines the URL path rewrite rules using regexp.Rexexp with captures
|
// RegexRules defines the URL path rewrite rules using regexp.Rexexp with captures
|
||||||
// Every capture group in the values can be retrieved by index e.g. $1, $2 and so on.
|
// Every capture group in the values can be retrieved by index e.g. $1, $2 and so on.
|
||||||
// Example:
|
// Example:
|
||||||
// "^/old/[0.9]+/": "/new",
|
// "^/old/[0.9]+/": "/new",
|
||||||
// "^/api/.+?/(.*)": "/v2/$1",
|
// "^/api/.+?/(.*)": "/v2/$1",
|
||||||
RegexRules map[*regexp.Regexp]string `yaml:"regex_rules"`
|
RegexRules map[*regexp.Regexp]string `yaml:"-"`
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
// DefaultRewriteConfig is the default Rewrite middleware config.
|
||||||
// DefaultRewriteConfig is the default Rewrite middleware config.
|
var DefaultRewriteConfig = RewriteConfig{
|
||||||
DefaultRewriteConfig = RewriteConfig{
|
Skipper: DefaultSkipper,
|
||||||
Skipper: DefaultSkipper,
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Rewrite returns a Rewrite middleware.
|
// Rewrite returns a Rewrite middleware.
|
||||||
//
|
//
|
||||||
|
135
vendor/github.com/labstack/echo/v4/middleware/secure.go
generated
vendored
135
vendor/github.com/labstack/echo/v4/middleware/secure.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -6,84 +9,80 @@ import (
|
|||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// SecureConfig defines the config for Secure middleware.
|
||||||
// SecureConfig defines the config for Secure middleware.
|
type SecureConfig struct {
|
||||||
SecureConfig struct {
|
// Skipper defines a function to skip middleware.
|
||||||
// Skipper defines a function to skip middleware.
|
Skipper Skipper
|
||||||
Skipper Skipper
|
|
||||||
|
|
||||||
// XSSProtection provides protection against cross-site scripting attack (XSS)
|
// XSSProtection provides protection against cross-site scripting attack (XSS)
|
||||||
// by setting the `X-XSS-Protection` header.
|
// by setting the `X-XSS-Protection` header.
|
||||||
// Optional. Default value "1; mode=block".
|
// Optional. Default value "1; mode=block".
|
||||||
XSSProtection string `yaml:"xss_protection"`
|
XSSProtection string `yaml:"xss_protection"`
|
||||||
|
|
||||||
// ContentTypeNosniff provides protection against overriding Content-Type
|
// ContentTypeNosniff provides protection against overriding Content-Type
|
||||||
// header by setting the `X-Content-Type-Options` header.
|
// header by setting the `X-Content-Type-Options` header.
|
||||||
// Optional. Default value "nosniff".
|
// Optional. Default value "nosniff".
|
||||||
ContentTypeNosniff string `yaml:"content_type_nosniff"`
|
ContentTypeNosniff string `yaml:"content_type_nosniff"`
|
||||||
|
|
||||||
// XFrameOptions can be used to indicate whether or not a browser should
|
// XFrameOptions can be used to indicate whether or not a browser should
|
||||||
// be allowed to render a page in a <frame>, <iframe> or <object> .
|
// be allowed to render a page in a <frame>, <iframe> or <object> .
|
||||||
// Sites can use this to avoid clickjacking attacks, by ensuring that their
|
// Sites can use this to avoid clickjacking attacks, by ensuring that their
|
||||||
// content is not embedded into other sites.provides protection against
|
// content is not embedded into other sites.provides protection against
|
||||||
// clickjacking.
|
// clickjacking.
|
||||||
// Optional. Default value "SAMEORIGIN".
|
// Optional. Default value "SAMEORIGIN".
|
||||||
// Possible values:
|
// Possible values:
|
||||||
// - "SAMEORIGIN" - The page can only be displayed in a frame on the same origin as the page itself.
|
// - "SAMEORIGIN" - The page can only be displayed in a frame on the same origin as the page itself.
|
||||||
// - "DENY" - The page cannot be displayed in a frame, regardless of the site attempting to do so.
|
// - "DENY" - The page cannot be displayed in a frame, regardless of the site attempting to do so.
|
||||||
// - "ALLOW-FROM uri" - The page can only be displayed in a frame on the specified origin.
|
// - "ALLOW-FROM uri" - The page can only be displayed in a frame on the specified origin.
|
||||||
XFrameOptions string `yaml:"x_frame_options"`
|
XFrameOptions string `yaml:"x_frame_options"`
|
||||||
|
|
||||||
// HSTSMaxAge sets the `Strict-Transport-Security` header to indicate how
|
// HSTSMaxAge sets the `Strict-Transport-Security` header to indicate how
|
||||||
// long (in seconds) browsers should remember that this site is only to
|
// long (in seconds) browsers should remember that this site is only to
|
||||||
// be accessed using HTTPS. This reduces your exposure to some SSL-stripping
|
// be accessed using HTTPS. This reduces your exposure to some SSL-stripping
|
||||||
// man-in-the-middle (MITM) attacks.
|
// man-in-the-middle (MITM) attacks.
|
||||||
// Optional. Default value 0.
|
// Optional. Default value 0.
|
||||||
HSTSMaxAge int `yaml:"hsts_max_age"`
|
HSTSMaxAge int `yaml:"hsts_max_age"`
|
||||||
|
|
||||||
// HSTSExcludeSubdomains won't include subdomains tag in the `Strict Transport Security`
|
// HSTSExcludeSubdomains won't include subdomains tag in the `Strict Transport Security`
|
||||||
// header, excluding all subdomains from security policy. It has no effect
|
// header, excluding all subdomains from security policy. It has no effect
|
||||||
// unless HSTSMaxAge is set to a non-zero value.
|
// unless HSTSMaxAge is set to a non-zero value.
|
||||||
// Optional. Default value false.
|
// Optional. Default value false.
|
||||||
HSTSExcludeSubdomains bool `yaml:"hsts_exclude_subdomains"`
|
HSTSExcludeSubdomains bool `yaml:"hsts_exclude_subdomains"`
|
||||||
|
|
||||||
// ContentSecurityPolicy sets the `Content-Security-Policy` header providing
|
// ContentSecurityPolicy sets the `Content-Security-Policy` header providing
|
||||||
// security against cross-site scripting (XSS), clickjacking and other code
|
// security against cross-site scripting (XSS), clickjacking and other code
|
||||||
// injection attacks resulting from execution of malicious content in the
|
// injection attacks resulting from execution of malicious content in the
|
||||||
// trusted web page context.
|
// trusted web page context.
|
||||||
// Optional. Default value "".
|
// Optional. Default value "".
|
||||||
ContentSecurityPolicy string `yaml:"content_security_policy"`
|
ContentSecurityPolicy string `yaml:"content_security_policy"`
|
||||||
|
|
||||||
// CSPReportOnly would use the `Content-Security-Policy-Report-Only` header instead
|
// CSPReportOnly would use the `Content-Security-Policy-Report-Only` header instead
|
||||||
// of the `Content-Security-Policy` header. This allows iterative updates of the
|
// of the `Content-Security-Policy` header. This allows iterative updates of the
|
||||||
// content security policy by only reporting the violations that would
|
// content security policy by only reporting the violations that would
|
||||||
// have occurred instead of blocking the resource.
|
// have occurred instead of blocking the resource.
|
||||||
// Optional. Default value false.
|
// Optional. Default value false.
|
||||||
CSPReportOnly bool `yaml:"csp_report_only"`
|
CSPReportOnly bool `yaml:"csp_report_only"`
|
||||||
|
|
||||||
// HSTSPreloadEnabled will add the preload tag in the `Strict Transport Security`
|
// HSTSPreloadEnabled will add the preload tag in the `Strict Transport Security`
|
||||||
// header, which enables the domain to be included in the HSTS preload list
|
// header, which enables the domain to be included in the HSTS preload list
|
||||||
// maintained by Chrome (and used by Firefox and Safari): https://hstspreload.org/
|
// maintained by Chrome (and used by Firefox and Safari): https://hstspreload.org/
|
||||||
// Optional. Default value false.
|
// Optional. Default value false.
|
||||||
HSTSPreloadEnabled bool `yaml:"hsts_preload_enabled"`
|
HSTSPreloadEnabled bool `yaml:"hsts_preload_enabled"`
|
||||||
|
|
||||||
// ReferrerPolicy sets the `Referrer-Policy` header providing security against
|
// ReferrerPolicy sets the `Referrer-Policy` header providing security against
|
||||||
// leaking potentially sensitive request paths to third parties.
|
// leaking potentially sensitive request paths to third parties.
|
||||||
// Optional. Default value "".
|
// Optional. Default value "".
|
||||||
ReferrerPolicy string `yaml:"referrer_policy"`
|
ReferrerPolicy string `yaml:"referrer_policy"`
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
// DefaultSecureConfig is the default Secure middleware config.
|
||||||
// DefaultSecureConfig is the default Secure middleware config.
|
var DefaultSecureConfig = SecureConfig{
|
||||||
DefaultSecureConfig = SecureConfig{
|
Skipper: DefaultSkipper,
|
||||||
Skipper: DefaultSkipper,
|
XSSProtection: "1; mode=block",
|
||||||
XSSProtection: "1; mode=block",
|
ContentTypeNosniff: "nosniff",
|
||||||
ContentTypeNosniff: "nosniff",
|
XFrameOptions: "SAMEORIGIN",
|
||||||
XFrameOptions: "SAMEORIGIN",
|
HSTSPreloadEnabled: false,
|
||||||
HSTSPreloadEnabled: false,
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Secure returns a Secure middleware.
|
// Secure returns a Secure middleware.
|
||||||
// Secure middleware provides protection against cross-site scripting (XSS) attack,
|
// Secure middleware provides protection against cross-site scripting (XSS) attack,
|
||||||
|
31
vendor/github.com/labstack/echo/v4/middleware/slash.go
generated
vendored
31
vendor/github.com/labstack/echo/v4/middleware/slash.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -6,24 +9,20 @@ import (
|
|||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// TrailingSlashConfig defines the config for TrailingSlash middleware.
|
||||||
// TrailingSlashConfig defines the config for TrailingSlash middleware.
|
type TrailingSlashConfig struct {
|
||||||
TrailingSlashConfig struct {
|
// Skipper defines a function to skip middleware.
|
||||||
// Skipper defines a function to skip middleware.
|
Skipper Skipper
|
||||||
Skipper Skipper
|
|
||||||
|
|
||||||
// Status code to be used when redirecting the request.
|
// Status code to be used when redirecting the request.
|
||||||
// Optional, but when provided the request is redirected using this code.
|
// Optional, but when provided the request is redirected using this code.
|
||||||
RedirectCode int `yaml:"redirect_code"`
|
RedirectCode int `yaml:"redirect_code"`
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
// DefaultTrailingSlashConfig is the default TrailingSlash middleware config.
|
||||||
// DefaultTrailingSlashConfig is the default TrailingSlash middleware config.
|
var DefaultTrailingSlashConfig = TrailingSlashConfig{
|
||||||
DefaultTrailingSlashConfig = TrailingSlashConfig{
|
Skipper: DefaultSkipper,
|
||||||
Skipper: DefaultSkipper,
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// AddTrailingSlash returns a root level (before router) middleware which adds a
|
// AddTrailingSlash returns a root level (before router) middleware which adds a
|
||||||
// trailing slash to the request `URL#Path`.
|
// trailing slash to the request `URL#Path`.
|
||||||
|
97
vendor/github.com/labstack/echo/v4/middleware/static.go
generated
vendored
97
vendor/github.com/labstack/echo/v4/middleware/static.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -8,47 +11,44 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"github.com/labstack/gommon/bytes"
|
"github.com/labstack/gommon/bytes"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// StaticConfig defines the config for Static middleware.
|
||||||
// StaticConfig defines the config for Static middleware.
|
type StaticConfig struct {
|
||||||
StaticConfig struct {
|
// Skipper defines a function to skip middleware.
|
||||||
// Skipper defines a function to skip middleware.
|
Skipper Skipper
|
||||||
Skipper Skipper
|
|
||||||
|
|
||||||
// Root directory from where the static content is served.
|
// Root directory from where the static content is served.
|
||||||
// Required.
|
// Required.
|
||||||
Root string `yaml:"root"`
|
Root string `yaml:"root"`
|
||||||
|
|
||||||
// Index file for serving a directory.
|
// Index file for serving a directory.
|
||||||
// Optional. Default value "index.html".
|
// Optional. Default value "index.html".
|
||||||
Index string `yaml:"index"`
|
Index string `yaml:"index"`
|
||||||
|
|
||||||
// Enable HTML5 mode by forwarding all not-found requests to root so that
|
// Enable HTML5 mode by forwarding all not-found requests to root so that
|
||||||
// SPA (single-page application) can handle the routing.
|
// SPA (single-page application) can handle the routing.
|
||||||
// Optional. Default value false.
|
// Optional. Default value false.
|
||||||
HTML5 bool `yaml:"html5"`
|
HTML5 bool `yaml:"html5"`
|
||||||
|
|
||||||
// Enable directory browsing.
|
// Enable directory browsing.
|
||||||
// Optional. Default value false.
|
// Optional. Default value false.
|
||||||
Browse bool `yaml:"browse"`
|
Browse bool `yaml:"browse"`
|
||||||
|
|
||||||
// Enable ignoring of the base of the URL path.
|
// Enable ignoring of the base of the URL path.
|
||||||
// Example: when assigning a static middleware to a non root path group,
|
// Example: when assigning a static middleware to a non root path group,
|
||||||
// the filesystem path is not doubled
|
// the filesystem path is not doubled
|
||||||
// Optional. Default value false.
|
// Optional. Default value false.
|
||||||
IgnoreBase bool `yaml:"ignoreBase"`
|
IgnoreBase bool `yaml:"ignoreBase"`
|
||||||
|
|
||||||
// Filesystem provides access to the static content.
|
// Filesystem provides access to the static content.
|
||||||
// Optional. Defaults to http.Dir(config.Root)
|
// Optional. Defaults to http.Dir(config.Root)
|
||||||
Filesystem http.FileSystem `yaml:"-"`
|
Filesystem http.FileSystem `yaml:"-"`
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
const html = `
|
const html = `
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
@ -122,13 +122,11 @@ const html = `
|
|||||||
</html>
|
</html>
|
||||||
`
|
`
|
||||||
|
|
||||||
var (
|
// DefaultStaticConfig is the default Static middleware config.
|
||||||
// DefaultStaticConfig is the default Static middleware config.
|
var DefaultStaticConfig = StaticConfig{
|
||||||
DefaultStaticConfig = StaticConfig{
|
Skipper: DefaultSkipper,
|
||||||
Skipper: DefaultSkipper,
|
Index: "index.html",
|
||||||
Index: "index.html",
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Static returns a Static middleware to serves static content from the provided
|
// Static returns a Static middleware to serves static content from the provided
|
||||||
// root directory.
|
// root directory.
|
||||||
@ -157,9 +155,9 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Index template
|
// Index template
|
||||||
t, err := template.New("index").Parse(html)
|
t, tErr := template.New("index").Parse(html)
|
||||||
if err != nil {
|
if tErr != nil {
|
||||||
panic(fmt.Sprintf("echo: %v", err))
|
panic(fmt.Errorf("echo: %w", tErr))
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
@ -176,7 +174,7 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
name := filepath.Join(config.Root, filepath.Clean("/"+p)) // "/"+ for security
|
name := path.Join(config.Root, path.Clean("/"+p)) // "/"+ for security
|
||||||
|
|
||||||
if config.IgnoreBase {
|
if config.IgnoreBase {
|
||||||
routePath := path.Base(strings.TrimRight(c.Path(), "/*"))
|
routePath := path.Base(strings.TrimRight(c.Path(), "/*"))
|
||||||
@ -187,12 +185,14 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := openFile(config.Filesystem, name)
|
file, err := config.Filesystem.Open(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !os.IsNotExist(err) {
|
if !isIgnorableOpenFileError(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// file with that path did not exist, so we continue down in middleware/handler chain, hoping that we end up in
|
||||||
|
// handler that is meant to handle this request
|
||||||
if err = next(c); err == nil {
|
if err = next(c); err == nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -202,7 +202,7 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err = openFile(config.Filesystem, filepath.Join(config.Root, config.Index))
|
file, err = config.Filesystem.Open(path.Join(config.Root, config.Index))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -216,15 +216,13 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if info.IsDir() {
|
if info.IsDir() {
|
||||||
index, err := openFile(config.Filesystem, filepath.Join(name, config.Index))
|
index, err := config.Filesystem.Open(path.Join(name, config.Index))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if config.Browse {
|
if config.Browse {
|
||||||
return listDir(t, name, file, c.Response())
|
return listDir(t, name, file, c.Response())
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.IsNotExist(err) {
|
return next(c)
|
||||||
return next(c)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defer index.Close()
|
defer index.Close()
|
||||||
@ -242,11 +240,6 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func openFile(fs http.FileSystem, name string) (http.File, error) {
|
|
||||||
pathWithSlashes := filepath.ToSlash(name)
|
|
||||||
return fs.Open(pathWithSlashes)
|
|
||||||
}
|
|
||||||
|
|
||||||
func serveFile(c echo.Context, file http.File, info os.FileInfo) error {
|
func serveFile(c echo.Context, file http.File, info os.FileInfo) error {
|
||||||
http.ServeContent(c.Response(), c.Request(), info.Name(), info.ModTime(), file)
|
http.ServeContent(c.Response(), c.Request(), info.Name(), info.ModTime(), file)
|
||||||
return nil
|
return nil
|
||||||
|
15
vendor/github.com/labstack/echo/v4/middleware/static_other.go
generated
vendored
Normal file
15
vendor/github.com/labstack/echo/v4/middleware/static_other.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// We ignore these errors as there could be handler that matches request path.
|
||||||
|
func isIgnorableOpenFileError(err error) bool {
|
||||||
|
return os.IsNotExist(err)
|
||||||
|
}
|
26
vendor/github.com/labstack/echo/v4/middleware/static_windows.go
generated
vendored
Normal file
26
vendor/github.com/labstack/echo/v4/middleware/static_windows.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// We ignore these errors as there could be handler that matches request path.
|
||||||
|
//
|
||||||
|
// As of Go 1.20 filepath.Clean has different behaviour on OS related filesystems so we need to use path.Clean
|
||||||
|
// on Windows which has some caveats. The Open methods might return different errors than earlier versions and
|
||||||
|
// as of 1.20 path checks are more strict on the provided path and considers [UNC](https://en.wikipedia.org/wiki/Path_(computing)#UNC)
|
||||||
|
// paths with missing host etc parts as invalid. Previously it would result you `fs.ErrNotExist`.
|
||||||
|
//
|
||||||
|
// For 1.20@Windows we need to treat those errors the same as `fs.ErrNotExists` so we can continue handling
|
||||||
|
// errors in the middleware/handler chain. Otherwise we might end up with status 500 instead of finding a route
|
||||||
|
// or return 404 not found.
|
||||||
|
func isIgnorableOpenFileError(err error) bool {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
errTxt := err.Error()
|
||||||
|
return errTxt == "http: invalid or unsafe file path" || errTxt == "invalid path"
|
||||||
|
}
|
17
vendor/github.com/labstack/echo/v4/middleware/timeout.go
generated
vendored
17
vendor/github.com/labstack/echo/v4/middleware/timeout.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -77,14 +80,12 @@ type TimeoutConfig struct {
|
|||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
// DefaultTimeoutConfig is the default Timeout middleware config.
|
||||||
// DefaultTimeoutConfig is the default Timeout middleware config.
|
var DefaultTimeoutConfig = TimeoutConfig{
|
||||||
DefaultTimeoutConfig = TimeoutConfig{
|
Skipper: DefaultSkipper,
|
||||||
Skipper: DefaultSkipper,
|
Timeout: 0,
|
||||||
Timeout: 0,
|
ErrorMessage: "",
|
||||||
ErrorMessage: "",
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Timeout returns a middleware which returns error (503 Service Unavailable error) to client immediately when handler
|
// Timeout returns a middleware which returns error (503 Service Unavailable error) to client immediately when handler
|
||||||
// call runs for longer than its time limit. NB: timeout does not stop handler execution.
|
// call runs for longer than its time limit. NB: timeout does not stop handler execution.
|
||||||
|
49
vendor/github.com/labstack/echo/v4/middleware/util.go
generated
vendored
49
vendor/github.com/labstack/echo/v4/middleware/util.go
generated
vendored
@ -1,7 +1,14 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"crypto/rand"
|
||||||
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
func matchScheme(domain, pattern string) bool {
|
func matchScheme(domain, pattern string) bool {
|
||||||
@ -52,3 +59,45 @@ func matchSubdomain(domain, pattern string) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://tip.golang.org/doc/go1.19#:~:text=Read%20no%20longer%20buffers%20random%20data%20obtained%20from%20the%20operating%20system%20between%20calls
|
||||||
|
var randomReaderPool = sync.Pool{New: func() interface{} {
|
||||||
|
return bufio.NewReader(rand.Reader)
|
||||||
|
}}
|
||||||
|
|
||||||
|
const randomStringCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||||
|
const randomStringCharsetLen = 52 // len(randomStringCharset)
|
||||||
|
const randomStringMaxByte = 255 - (256 % randomStringCharsetLen)
|
||||||
|
|
||||||
|
func randomString(length uint8) string {
|
||||||
|
reader := randomReaderPool.Get().(*bufio.Reader)
|
||||||
|
defer randomReaderPool.Put(reader)
|
||||||
|
|
||||||
|
b := make([]byte, length)
|
||||||
|
r := make([]byte, length+(length/4)) // perf: avoid read from rand.Reader many times
|
||||||
|
var i uint8 = 0
|
||||||
|
|
||||||
|
// security note:
|
||||||
|
// we can't just simply do b[i]=randomStringCharset[rb%len(randomStringCharset)],
|
||||||
|
// len(len(randomStringCharset)) is 52, and rb is [0, 255], 256 = 52 * 4 + 48.
|
||||||
|
// make the first 48 characters more possibly to be generated then others.
|
||||||
|
// So we have to skip bytes when rb > randomStringMaxByte
|
||||||
|
|
||||||
|
for {
|
||||||
|
_, err := io.ReadFull(reader, r)
|
||||||
|
if err != nil {
|
||||||
|
panic("unexpected error happened when reading from bufio.NewReader(crypto/rand.Reader)")
|
||||||
|
}
|
||||||
|
for _, rb := range r {
|
||||||
|
if rb > randomStringMaxByte {
|
||||||
|
// Skip this number to avoid bias.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
b[i] = randomStringCharset[rb%randomStringCharsetLen]
|
||||||
|
i++
|
||||||
|
if i == length {
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
44
vendor/github.com/labstack/echo/v4/response.go
generated
vendored
44
vendor/github.com/labstack/echo/v4/response.go
generated
vendored
@ -1,25 +1,27 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package echo
|
package echo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// Response wraps an http.ResponseWriter and implements its interface to be used
|
||||||
// Response wraps an http.ResponseWriter and implements its interface to be used
|
// by an HTTP handler to construct an HTTP response.
|
||||||
// by an HTTP handler to construct an HTTP response.
|
// See: https://golang.org/pkg/net/http/#ResponseWriter
|
||||||
// See: https://golang.org/pkg/net/http/#ResponseWriter
|
type Response struct {
|
||||||
Response struct {
|
echo *Echo
|
||||||
echo *Echo
|
beforeFuncs []func()
|
||||||
beforeFuncs []func()
|
afterFuncs []func()
|
||||||
afterFuncs []func()
|
Writer http.ResponseWriter
|
||||||
Writer http.ResponseWriter
|
Status int
|
||||||
Status int
|
Size int64
|
||||||
Size int64
|
Committed bool
|
||||||
Committed bool
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewResponse creates a new instance of Response.
|
// NewResponse creates a new instance of Response.
|
||||||
func NewResponse(w http.ResponseWriter, e *Echo) (r *Response) {
|
func NewResponse(w http.ResponseWriter, e *Echo) (r *Response) {
|
||||||
@ -84,14 +86,24 @@ func (r *Response) Write(b []byte) (n int, err error) {
|
|||||||
// buffered data to the client.
|
// buffered data to the client.
|
||||||
// See [http.Flusher](https://golang.org/pkg/net/http/#Flusher)
|
// See [http.Flusher](https://golang.org/pkg/net/http/#Flusher)
|
||||||
func (r *Response) Flush() {
|
func (r *Response) Flush() {
|
||||||
r.Writer.(http.Flusher).Flush()
|
err := responseControllerFlush(r.Writer)
|
||||||
|
if err != nil && errors.Is(err, http.ErrNotSupported) {
|
||||||
|
panic(errors.New("response writer flushing is not supported"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hijack implements the http.Hijacker interface to allow an HTTP handler to
|
// Hijack implements the http.Hijacker interface to allow an HTTP handler to
|
||||||
// take over the connection.
|
// take over the connection.
|
||||||
// See [http.Hijacker](https://golang.org/pkg/net/http/#Hijacker)
|
// See [http.Hijacker](https://golang.org/pkg/net/http/#Hijacker)
|
||||||
func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||||
return r.Writer.(http.Hijacker).Hijack()
|
return responseControllerHijack(r.Writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap returns the original http.ResponseWriter.
|
||||||
|
// ResponseController can be used to access the original http.ResponseWriter.
|
||||||
|
// See [https://go.dev/blog/go1.20]
|
||||||
|
func (r *Response) Unwrap() http.ResponseWriter {
|
||||||
|
return r.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Response) reset(w http.ResponseWriter) {
|
func (r *Response) reset(w http.ResponseWriter) {
|
||||||
|
44
vendor/github.com/labstack/echo/v4/responsecontroller_1.19.go
generated
vendored
Normal file
44
vendor/github.com/labstack/echo/v4/responsecontroller_1.19.go
generated
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
|
//go:build !go1.20
|
||||||
|
|
||||||
|
package echo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: remove when Go 1.23 is released and we do not support 1.19 anymore
|
||||||
|
func responseControllerFlush(rw http.ResponseWriter) error {
|
||||||
|
for {
|
||||||
|
switch t := rw.(type) {
|
||||||
|
case interface{ FlushError() error }:
|
||||||
|
return t.FlushError()
|
||||||
|
case http.Flusher:
|
||||||
|
t.Flush()
|
||||||
|
return nil
|
||||||
|
case interface{ Unwrap() http.ResponseWriter }:
|
||||||
|
rw = t.Unwrap()
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("%w", http.ErrNotSupported)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove when Go 1.23 is released and we do not support 1.19 anymore
|
||||||
|
func responseControllerHijack(rw http.ResponseWriter) (net.Conn, *bufio.ReadWriter, error) {
|
||||||
|
for {
|
||||||
|
switch t := rw.(type) {
|
||||||
|
case http.Hijacker:
|
||||||
|
return t.Hijack()
|
||||||
|
case interface{ Unwrap() http.ResponseWriter }:
|
||||||
|
rw = t.Unwrap()
|
||||||
|
default:
|
||||||
|
return nil, nil, fmt.Errorf("%w", http.ErrNotSupported)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
vendor/github.com/labstack/echo/v4/responsecontroller_1.20.go
generated
vendored
Normal file
20
vendor/github.com/labstack/echo/v4/responsecontroller_1.20.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
|
//go:build go1.20
|
||||||
|
|
||||||
|
package echo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func responseControllerFlush(rw http.ResponseWriter) error {
|
||||||
|
return http.NewResponseController(rw).Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func responseControllerHijack(rw http.ResponseWriter) (net.Conn, *bufio.ReadWriter, error) {
|
||||||
|
return http.NewResponseController(rw).Hijack()
|
||||||
|
}
|
151
vendor/github.com/labstack/echo/v4/router.go
generated
vendored
151
vendor/github.com/labstack/echo/v4/router.go
generated
vendored
@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
||||||
|
|
||||||
package echo
|
package echo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -6,56 +9,58 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// Router is the registry of all registered routes for an `Echo` instance for
|
||||||
// Router is the registry of all registered routes for an `Echo` instance for
|
// request matching and URL path parameter parsing.
|
||||||
// request matching and URL path parameter parsing.
|
type Router struct {
|
||||||
Router struct {
|
tree *node
|
||||||
tree *node
|
routes map[string]*Route
|
||||||
routes map[string]*Route
|
echo *Echo
|
||||||
echo *Echo
|
}
|
||||||
}
|
|
||||||
node struct {
|
|
||||||
kind kind
|
|
||||||
label byte
|
|
||||||
prefix string
|
|
||||||
parent *node
|
|
||||||
staticChildren children
|
|
||||||
originalPath string
|
|
||||||
methods *routeMethods
|
|
||||||
paramChild *node
|
|
||||||
anyChild *node
|
|
||||||
paramsCount int
|
|
||||||
// isLeaf indicates that node does not have child routes
|
|
||||||
isLeaf bool
|
|
||||||
// isHandler indicates that node has at least one handler registered to it
|
|
||||||
isHandler bool
|
|
||||||
|
|
||||||
// notFoundHandler is handler registered with RouteNotFound method and is executed for 404 cases
|
type node struct {
|
||||||
notFoundHandler *routeMethod
|
kind kind
|
||||||
}
|
label byte
|
||||||
kind uint8
|
prefix string
|
||||||
children []*node
|
parent *node
|
||||||
routeMethod struct {
|
staticChildren children
|
||||||
ppath string
|
originalPath string
|
||||||
pnames []string
|
methods *routeMethods
|
||||||
handler HandlerFunc
|
paramChild *node
|
||||||
}
|
anyChild *node
|
||||||
routeMethods struct {
|
paramsCount int
|
||||||
connect *routeMethod
|
// isLeaf indicates that node does not have child routes
|
||||||
delete *routeMethod
|
isLeaf bool
|
||||||
get *routeMethod
|
// isHandler indicates that node has at least one handler registered to it
|
||||||
head *routeMethod
|
isHandler bool
|
||||||
options *routeMethod
|
|
||||||
patch *routeMethod
|
// notFoundHandler is handler registered with RouteNotFound method and is executed for 404 cases
|
||||||
post *routeMethod
|
notFoundHandler *routeMethod
|
||||||
propfind *routeMethod
|
}
|
||||||
put *routeMethod
|
|
||||||
trace *routeMethod
|
type kind uint8
|
||||||
report *routeMethod
|
type children []*node
|
||||||
anyOther map[string]*routeMethod
|
|
||||||
allowHeader string
|
type routeMethod struct {
|
||||||
}
|
ppath string
|
||||||
)
|
pnames []string
|
||||||
|
handler HandlerFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
type routeMethods struct {
|
||||||
|
connect *routeMethod
|
||||||
|
delete *routeMethod
|
||||||
|
get *routeMethod
|
||||||
|
head *routeMethod
|
||||||
|
options *routeMethod
|
||||||
|
patch *routeMethod
|
||||||
|
post *routeMethod
|
||||||
|
propfind *routeMethod
|
||||||
|
put *routeMethod
|
||||||
|
trace *routeMethod
|
||||||
|
report *routeMethod
|
||||||
|
anyOther map[string]*routeMethod
|
||||||
|
allowHeader string
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
staticKind kind = iota
|
staticKind kind = iota
|
||||||
@ -151,7 +156,7 @@ func (r *Router) Routes() []*Route {
|
|||||||
return routes
|
return routes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reverse generates an URL from route name and provided parameters.
|
// Reverse generates a URL from route name and provided parameters.
|
||||||
func (r *Router) Reverse(name string, params ...interface{}) string {
|
func (r *Router) Reverse(name string, params ...interface{}) string {
|
||||||
uri := new(bytes.Buffer)
|
uri := new(bytes.Buffer)
|
||||||
ln := len(params)
|
ln := len(params)
|
||||||
@ -159,7 +164,12 @@ func (r *Router) Reverse(name string, params ...interface{}) string {
|
|||||||
for _, route := range r.routes {
|
for _, route := range r.routes {
|
||||||
if route.Name == name {
|
if route.Name == name {
|
||||||
for i, l := 0, len(route.Path); i < l; i++ {
|
for i, l := 0, len(route.Path); i < l; i++ {
|
||||||
if (route.Path[i] == ':' || route.Path[i] == '*') && n < ln {
|
hasBackslash := route.Path[i] == '\\'
|
||||||
|
if hasBackslash && i+1 < l && route.Path[i+1] == ':' {
|
||||||
|
i++ // backslash before colon escapes that colon. in that case skip backslash
|
||||||
|
}
|
||||||
|
if n < ln && (route.Path[i] == '*' || (!hasBackslash && route.Path[i] == ':')) {
|
||||||
|
// in case of `*` wildcard or `:` (unescaped colon) param we replace everything till next slash or end of path
|
||||||
for ; i < l && route.Path[i] != '/'; i++ {
|
for ; i < l && route.Path[i] != '/'; i++ {
|
||||||
}
|
}
|
||||||
uri.WriteString(fmt.Sprintf("%v", params[n]))
|
uri.WriteString(fmt.Sprintf("%v", params[n]))
|
||||||
@ -175,8 +185,18 @@ func (r *Router) Reverse(name string, params ...interface{}) string {
|
|||||||
return uri.String()
|
return uri.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func normalizePathSlash(path string) string {
|
||||||
|
if path == "" {
|
||||||
|
path = "/"
|
||||||
|
} else if path[0] != '/' {
|
||||||
|
path = "/" + path
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Router) add(method, path, name string, h HandlerFunc) *Route {
|
func (r *Router) add(method, path, name string, h HandlerFunc) *Route {
|
||||||
r.Add(method, path, h)
|
path = normalizePathSlash(path)
|
||||||
|
r.insert(method, path, h)
|
||||||
|
|
||||||
route := &Route{
|
route := &Route{
|
||||||
Method: method,
|
Method: method,
|
||||||
@ -189,13 +209,11 @@ func (r *Router) add(method, path, name string, h HandlerFunc) *Route {
|
|||||||
|
|
||||||
// Add registers a new route for method and path with matching handler.
|
// Add registers a new route for method and path with matching handler.
|
||||||
func (r *Router) Add(method, path string, h HandlerFunc) {
|
func (r *Router) Add(method, path string, h HandlerFunc) {
|
||||||
// Validate path
|
r.insert(method, normalizePathSlash(path), h)
|
||||||
if path == "" {
|
}
|
||||||
path = "/"
|
|
||||||
}
|
func (r *Router) insert(method, path string, h HandlerFunc) {
|
||||||
if path[0] != '/' {
|
path = normalizePathSlash(path)
|
||||||
path = "/" + path
|
|
||||||
}
|
|
||||||
pnames := []string{} // Param names
|
pnames := []string{} // Param names
|
||||||
ppath := path // Pristine path
|
ppath := path // Pristine path
|
||||||
|
|
||||||
@ -214,7 +232,7 @@ func (r *Router) Add(method, path string, h HandlerFunc) {
|
|||||||
}
|
}
|
||||||
j := i + 1
|
j := i + 1
|
||||||
|
|
||||||
r.insert(method, path[:i], staticKind, routeMethod{})
|
r.insertNode(method, path[:i], staticKind, routeMethod{})
|
||||||
for ; i < lcpIndex && path[i] != '/'; i++ {
|
for ; i < lcpIndex && path[i] != '/'; i++ {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,21 +242,21 @@ func (r *Router) Add(method, path string, h HandlerFunc) {
|
|||||||
|
|
||||||
if i == lcpIndex {
|
if i == lcpIndex {
|
||||||
// path node is last fragment of route path. ie. `/users/:id`
|
// path node is last fragment of route path. ie. `/users/:id`
|
||||||
r.insert(method, path[:i], paramKind, routeMethod{ppath, pnames, h})
|
r.insertNode(method, path[:i], paramKind, routeMethod{ppath, pnames, h})
|
||||||
} else {
|
} else {
|
||||||
r.insert(method, path[:i], paramKind, routeMethod{})
|
r.insertNode(method, path[:i], paramKind, routeMethod{})
|
||||||
}
|
}
|
||||||
} else if path[i] == '*' {
|
} else if path[i] == '*' {
|
||||||
r.insert(method, path[:i], staticKind, routeMethod{})
|
r.insertNode(method, path[:i], staticKind, routeMethod{})
|
||||||
pnames = append(pnames, "*")
|
pnames = append(pnames, "*")
|
||||||
r.insert(method, path[:i+1], anyKind, routeMethod{ppath, pnames, h})
|
r.insertNode(method, path[:i+1], anyKind, routeMethod{ppath, pnames, h})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r.insert(method, path, staticKind, routeMethod{ppath, pnames, h})
|
r.insertNode(method, path, staticKind, routeMethod{ppath, pnames, h})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) insert(method, path string, t kind, rm routeMethod) {
|
func (r *Router) insertNode(method, path string, t kind, rm routeMethod) {
|
||||||
// Adjust max param
|
// Adjust max param
|
||||||
paramLen := len(rm.pnames)
|
paramLen := len(rm.pnames)
|
||||||
if *r.echo.maxParam < paramLen {
|
if *r.echo.maxParam < paramLen {
|
||||||
@ -524,7 +542,6 @@ func optionsMethodHandler(allowMethods string) func(c Context) error {
|
|||||||
// - Return it `Echo#ReleaseContext()`.
|
// - Return it `Echo#ReleaseContext()`.
|
||||||
func (r *Router) Find(method, path string, c Context) {
|
func (r *Router) Find(method, path string, c Context) {
|
||||||
ctx := c.(*context)
|
ctx := c.(*context)
|
||||||
ctx.path = path
|
|
||||||
currentNode := r.tree // Current node as root
|
currentNode := r.tree // Current node as root
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
48
vendor/github.com/labstack/gommon/random/random.go
generated
vendored
48
vendor/github.com/labstack/gommon/random/random.go
generated
vendored
@ -1,48 +0,0 @@
|
|||||||
package random
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/rand"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
Random struct {
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Charsets
|
|
||||||
const (
|
|
||||||
Uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
||||||
Lowercase = "abcdefghijklmnopqrstuvwxyz"
|
|
||||||
Alphabetic = Uppercase + Lowercase
|
|
||||||
Numeric = "0123456789"
|
|
||||||
Alphanumeric = Alphabetic + Numeric
|
|
||||||
Symbols = "`" + `~!@#$%^&*()-_+={}[]|\;:"<>,./?`
|
|
||||||
Hex = Numeric + "abcdef"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
global = New()
|
|
||||||
)
|
|
||||||
|
|
||||||
func New() *Random {
|
|
||||||
rand.Seed(time.Now().UnixNano())
|
|
||||||
return new(Random)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Random) String(length uint8, charsets ...string) string {
|
|
||||||
charset := strings.Join(charsets, "")
|
|
||||||
if charset == "" {
|
|
||||||
charset = Alphanumeric
|
|
||||||
}
|
|
||||||
b := make([]byte, length)
|
|
||||||
for i := range b {
|
|
||||||
b[i] = charset[rand.Int63()%int64(len(charset))]
|
|
||||||
}
|
|
||||||
return string(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func String(length uint8, charsets ...string) string {
|
|
||||||
return global.String(length, charsets...)
|
|
||||||
}
|
|
4
vendor/github.com/likexian/gokit/LICENSE
generated
vendored
4
vendor/github.com/likexian/gokit/LICENSE
generated
vendored
@ -186,7 +186,7 @@
|
|||||||
same "printed page" as the copyright notice for easier
|
same "printed page" as the copyright notice for easier
|
||||||
identification within third-party archives.
|
identification within third-party archives.
|
||||||
|
|
||||||
Copyright 2012-2022 Li Kexian
|
Copyright 2012-2024 Li Kexian
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -200,6 +200,6 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|
||||||
APPENDIX: Copyright 2012-2022 Li Kexian
|
APPENDIX: Copyright 2012-2024 Li Kexian
|
||||||
|
|
||||||
https://www.likexian.com/
|
https://www.likexian.com/
|
||||||
|
2
vendor/github.com/likexian/gokit/assert/README.md
generated
vendored
2
vendor/github.com/likexian/gokit/assert/README.md
generated
vendored
@ -85,7 +85,7 @@ b := assert.If(a == 1, true, false)
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Copyright 2012-2022 [Li Kexian](https://www.likexian.com/)
|
Copyright 2012-2024 [Li Kexian](https://www.likexian.com/)
|
||||||
|
|
||||||
Licensed under the Apache License 2.0
|
Licensed under the Apache License 2.0
|
||||||
|
|
||||||
|
2
vendor/github.com/likexian/gokit/assert/assert.go
generated
vendored
2
vendor/github.com/likexian/gokit/assert/assert.go
generated
vendored
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012-2022 Li Kexian
|
* Copyright 2012-2024 Li Kexian
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
23
vendor/github.com/likexian/gokit/assert/values.go
generated
vendored
23
vendor/github.com/likexian/gokit/assert/values.go
generated
vendored
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012-2022 Li Kexian
|
* Copyright 2012-2024 Li Kexian
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -114,9 +114,10 @@ func IsContains(array interface{}, value interface{}) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IsMatch returns if value v contains any match of pattern r
|
// IsMatch returns if value v contains any match of pattern r
|
||||||
// IsMatch(regexp.MustCompile("v\d+"), "v100")
|
//
|
||||||
// IsMatch("v\d+", "v100")
|
// IsMatch(regexp.MustCompile("v\d+"), "v100")
|
||||||
// IsMatch("\d+\.\d+", 100.1)
|
// IsMatch("v\d+", "v100")
|
||||||
|
// IsMatch("\d+\.\d+", 100.1)
|
||||||
func IsMatch(r interface{}, v interface{}) bool {
|
func IsMatch(r interface{}, v interface{}) bool {
|
||||||
var re *regexp.Regexp
|
var re *regexp.Regexp
|
||||||
|
|
||||||
@ -173,9 +174,10 @@ func IsGe(x, y interface{}) bool {
|
|||||||
|
|
||||||
// Compare compare x and y, by operation
|
// Compare compare x and y, by operation
|
||||||
// It returns nil for true, ErrInvalid for invalid operation, err for false
|
// It returns nil for true, ErrInvalid for invalid operation, err for false
|
||||||
// Compare(1, 2, ">") // number compare -> true
|
//
|
||||||
// Compare("a", "a", ">=") // string compare -> true
|
// Compare(1, 2, ">") // number compare -> true
|
||||||
// Compare([]string{"a", "b"}, []string{"a"}, "<") // slice len compare -> false
|
// Compare("a", "a", ">=") // string compare -> true
|
||||||
|
// Compare([]string{"a", "b"}, []string{"a"}, "<") // slice len compare -> false
|
||||||
func Compare(x, y interface{}, op string) error { //nolint:cyclop
|
func Compare(x, y interface{}, op string) error { //nolint:cyclop
|
||||||
if !IsContains([]string{Comparer.LT, Comparer.LE, Comparer.GT, Comparer.GE}, op) {
|
if !IsContains([]string{Comparer.LT, Comparer.LE, Comparer.GT, Comparer.GE}, op) {
|
||||||
return ErrInvalid
|
return ErrInvalid
|
||||||
@ -322,9 +324,12 @@ func ToFloat64(v interface{}) (float64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If returns x if c is true, else y
|
// If returns x if c is true, else y
|
||||||
// z = If(c, x, y)
|
//
|
||||||
|
// z = If(c, x, y)
|
||||||
|
//
|
||||||
// equal to:
|
// equal to:
|
||||||
// z = c ? x : y
|
//
|
||||||
|
// z = c ? x : y
|
||||||
func If(c bool, x, y interface{}) interface{} {
|
func If(c bool, x, y interface{}) interface{} {
|
||||||
if c {
|
if c {
|
||||||
return x
|
return x
|
||||||
|
54
vendor/github.com/likexian/gokit/xrand/README.md
generated
vendored
Normal file
54
vendor/github.com/likexian/gokit/xrand/README.md
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# GoKit - xrand
|
||||||
|
|
||||||
|
Rand kits for Golang development.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
go get -u github.com/likexian/gokit
|
||||||
|
|
||||||
|
## Importing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/likexian/gokit/xrand"
|
||||||
|
)
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
Visit the docs on [GoDoc](https://godoc.org/github.com/likexian/gokit/xrand)
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
### Rand int between 0 and 10000
|
||||||
|
|
||||||
|
```go
|
||||||
|
n := xrand.Int(10000)
|
||||||
|
fmt.Println("rand int between 0 and 10000 is:", n)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rand int between 1000 and 10000
|
||||||
|
|
||||||
|
```go
|
||||||
|
n := xrand.IntRange(1000, 10000)
|
||||||
|
fmt.Println("rand int between 1000 and 10000 is:", n)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rand bytes with length of 10
|
||||||
|
|
||||||
|
```go
|
||||||
|
b, err := xrand.Bytes(10)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("rand bytes:", b)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Copyright 2012-2024 [Li Kexian](https://www.likexian.com/)
|
||||||
|
|
||||||
|
Licensed under the Apache License 2.0
|
||||||
|
|
||||||
|
## Donation
|
||||||
|
|
||||||
|
If this project is helpful, please share it with friends.
|
||||||
|
|
||||||
|
If you want to thank me, you can [give me a cup of coffee](https://www.likexian.com/donate/).
|
115
vendor/github.com/likexian/gokit/xrand/xrand.go
generated
vendored
Normal file
115
vendor/github.com/likexian/gokit/xrand/xrand.go
generated
vendored
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2024 Li Kexian
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* A toolkit for Golang development
|
||||||
|
* https://www.likexian.com/
|
||||||
|
*/
|
||||||
|
|
||||||
|
package xrand
|
||||||
|
|
||||||
|
import (
|
||||||
|
crand "crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Version returns package version
|
||||||
|
func Version() string {
|
||||||
|
return "0.2.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Author returns package author
|
||||||
|
func Author() string {
|
||||||
|
return "[Li Kexian](https://www.likexian.com/)"
|
||||||
|
}
|
||||||
|
|
||||||
|
// License returns package license
|
||||||
|
func License() string {
|
||||||
|
return "Licensed under the Apache License 2.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int returns random int in [0, max)
|
||||||
|
func Int(max int) int {
|
||||||
|
if max <= 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
seed := rand.NewSource(time.Now().UnixNano())
|
||||||
|
|
||||||
|
return rand.New(seed).Intn(max)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntRange returns random int in [min, max)
|
||||||
|
func IntRange(min, max int) int {
|
||||||
|
if min > max {
|
||||||
|
min, max = max, min
|
||||||
|
}
|
||||||
|
|
||||||
|
return Int(max-min) + min
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns n random string from 0-9,a-z,A-Z
|
||||||
|
func String(n int) string {
|
||||||
|
sources := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
return StringRange(n, sources)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringRange returns n random string base on source
|
||||||
|
func StringRange(n int, source string) string {
|
||||||
|
if source == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
ss := []rune(source)
|
||||||
|
bs := make([]rune, n)
|
||||||
|
for i := range bs {
|
||||||
|
bs[i] = ss[Int(len(ss))]
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(bs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns n random bytes
|
||||||
|
func Bytes(n int) (bs []byte, err error) {
|
||||||
|
bs = make([]byte, n)
|
||||||
|
_, err = crand.Read(bs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hex returns hex string of n random bytes
|
||||||
|
func Hex(n int) (ss string, err error) {
|
||||||
|
bs, err := Bytes(n)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ss = hex.EncodeToString(bs)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base64 returns base64 string of n random bytes
|
||||||
|
func Base64(n int) (ss string, err error) {
|
||||||
|
bs, err := Bytes(n)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ss = base64.StdEncoding.EncodeToString(bs)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
2
vendor/github.com/likexian/gokit/xslice/README.md
generated
vendored
2
vendor/github.com/likexian/gokit/xslice/README.md
generated
vendored
@ -34,7 +34,7 @@ fmt.Println("new array:", array)
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Copyright 2012-2022 [Li Kexian](https://www.likexian.com/)
|
Copyright 2012-2024 [Li Kexian](https://www.likexian.com/)
|
||||||
|
|
||||||
Licensed under the Apache License 2.0
|
Licensed under the Apache License 2.0
|
||||||
|
|
||||||
|
9
vendor/github.com/likexian/gokit/xslice/xslice.go
generated
vendored
9
vendor/github.com/likexian/gokit/xslice/xslice.go
generated
vendored
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012-2022 Li Kexian
|
* Copyright 2012-2024 Li Kexian
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -22,13 +22,14 @@ package xslice
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/rand"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/likexian/gokit/xrand"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Version returns package version
|
// Version returns package version
|
||||||
func Version() string {
|
func Version() string {
|
||||||
return "0.22.0"
|
return "0.23.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Author returns package author
|
// Author returns package author
|
||||||
@ -209,7 +210,7 @@ func Shuffle(v interface{}) {
|
|||||||
|
|
||||||
swap := reflect.Swapper(v)
|
swap := reflect.Swapper(v)
|
||||||
for i := vv.Len() - 1; i >= 1; i-- {
|
for i := vv.Len() - 1; i >= 1; i-- {
|
||||||
j := rand.Intn(i + 1)
|
j := xrand.Int(i + 1)
|
||||||
swap(i, j)
|
swap(i, j)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3
vendor/github.com/likexian/whois-parser/.golangci.yml
generated
vendored
3
vendor/github.com/likexian/whois-parser/.golangci.yml
generated
vendored
@ -9,16 +9,13 @@ run:
|
|||||||
linters:
|
linters:
|
||||||
disable-all: true
|
disable-all: true
|
||||||
enable:
|
enable:
|
||||||
- deadcode
|
|
||||||
- errcheck
|
- errcheck
|
||||||
- gosimple
|
- gosimple
|
||||||
- govet
|
- govet
|
||||||
- ineffassign
|
- ineffassign
|
||||||
- staticcheck
|
- staticcheck
|
||||||
- structcheck
|
|
||||||
- typecheck
|
- typecheck
|
||||||
- unused
|
- unused
|
||||||
- varcheck
|
|
||||||
- cyclop
|
- cyclop
|
||||||
- durationcheck
|
- durationcheck
|
||||||
- errname
|
- errname
|
||||||
|
4
vendor/github.com/likexian/whois-parser/LICENSE
generated
vendored
4
vendor/github.com/likexian/whois-parser/LICENSE
generated
vendored
@ -186,7 +186,7 @@
|
|||||||
same "printed page" as the copyright notice for easier
|
same "printed page" as the copyright notice for easier
|
||||||
identification within third-party archives.
|
identification within third-party archives.
|
||||||
|
|
||||||
Copyright 2014-2022 Li Kexian
|
Copyright 2014-2024 Li Kexian
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -200,6 +200,6 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|
||||||
APPENDIX: Copyright 2014-2022 Li Kexian
|
APPENDIX: Copyright 2014-2024 Li Kexian
|
||||||
|
|
||||||
https://www.likexian.com/
|
https://www.likexian.com/
|
||||||
|
2
vendor/github.com/likexian/whois-parser/README.md
generated
vendored
2
vendor/github.com/likexian/whois-parser/README.md
generated
vendored
@ -71,7 +71,7 @@ Please refer to [whois](https://github.com/likexian/whois)
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Copyright 2014-2022 [Li Kexian](https://www.likexian.com/)
|
Copyright 2014-2024 [Li Kexian](https://www.likexian.com/)
|
||||||
|
|
||||||
Licensed under the Apache License 2.0
|
Licensed under the Apache License 2.0
|
||||||
|
|
||||||
|
19
vendor/github.com/likexian/whois-parser/error.go
generated
vendored
19
vendor/github.com/likexian/whois-parser/error.go
generated
vendored
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2014-2022 Li Kexian
|
* Copyright 2014-2024 Li Kexian
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -66,6 +66,7 @@ func isNotFoundDomain(data string) bool {
|
|||||||
"no match",
|
"no match",
|
||||||
"not found",
|
"not found",
|
||||||
"not match",
|
"not match",
|
||||||
|
"not available",
|
||||||
"no data found",
|
"no data found",
|
||||||
"nothing found",
|
"nothing found",
|
||||||
"no entries found",
|
"no entries found",
|
||||||
@ -80,9 +81,10 @@ func isNotFoundDomain(data string) bool {
|
|||||||
return containsIn(strings.ToLower(data), notFoundKeys)
|
return containsIn(strings.ToLower(data), notFoundKeys)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var reBlank = regexp.MustCompile(`\s+`)
|
||||||
|
|
||||||
// isExtNotFoundDomain returns if domain is not found by extension
|
// isExtNotFoundDomain returns if domain is not found by extension
|
||||||
func isExtNotFoundDomain(data, extension string) bool {
|
func isExtNotFoundDomain(data, extension string) bool {
|
||||||
reBlank := regexp.MustCompile(`\s+`)
|
|
||||||
data = reBlank.ReplaceAllString(data, " ")
|
data = reBlank.ReplaceAllString(data, " ")
|
||||||
|
|
||||||
switch extension {
|
switch extension {
|
||||||
@ -114,6 +116,12 @@ func isExtNotFoundDomain(data, extension string) bool {
|
|||||||
if strings.Contains(data, "is available") {
|
if strings.Contains(data, "is available") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
case "nu":
|
||||||
|
fallthrough
|
||||||
|
case "se":
|
||||||
|
if strings.Contains(data, "not found") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
@ -159,6 +167,13 @@ func isLimitExceeded(data string) bool {
|
|||||||
limitExceedKeys := []string{
|
limitExceedKeys := []string{
|
||||||
"limit exceeded",
|
"limit exceeded",
|
||||||
"server too busy",
|
"server too busy",
|
||||||
|
"quota exceeded",
|
||||||
|
"exceeded the maximum allowable",
|
||||||
|
"exceeded your query limit",
|
||||||
|
"restricted due to excessive queries",
|
||||||
|
"due to query limit controls",
|
||||||
|
"you have exceeded your allotted number of",
|
||||||
|
"maximum daily connection limit reached",
|
||||||
}
|
}
|
||||||
|
|
||||||
return containsIn(strings.ToLower(data), limitExceedKeys)
|
return containsIn(strings.ToLower(data), limitExceedKeys)
|
||||||
|
24
vendor/github.com/likexian/whois-parser/parser.go
generated
vendored
24
vendor/github.com/likexian/whois-parser/parser.go
generated
vendored
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2014-2022 Li Kexian
|
* Copyright 2014-2024 Li Kexian
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -30,7 +30,7 @@ import (
|
|||||||
|
|
||||||
// Version returns package version
|
// Version returns package version
|
||||||
func Version() string {
|
func Version() string {
|
||||||
return "1.24.1"
|
return "1.24.18"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Author returns package author
|
// Author returns package author
|
||||||
@ -107,6 +107,9 @@ func Parse(text string) (whoisInfo WhoisInfo, err error) { //nolint:cyclop
|
|||||||
domain.ID = value
|
domain.ID = value
|
||||||
case "domain_name":
|
case "domain_name":
|
||||||
if domain.Domain == "" {
|
if domain.Domain == "" {
|
||||||
|
if firstSpace := strings.IndexByte(value, ' '); firstSpace > 0 {
|
||||||
|
value = value[:firstSpace]
|
||||||
|
}
|
||||||
domain.Domain = strings.ToLower(value)
|
domain.Domain = strings.ToLower(value)
|
||||||
domain.Punycode, _ = idna.ToASCII(domain.Domain)
|
domain.Punycode, _ = idna.ToASCII(domain.Domain)
|
||||||
}
|
}
|
||||||
@ -150,6 +153,8 @@ func Parse(text string) (whoisInfo WhoisInfo, err error) { //nolint:cyclop
|
|||||||
if !strings.Contains(name, " ") {
|
if !strings.Contains(name, " ") {
|
||||||
if name == "registrar" {
|
if name == "registrar" {
|
||||||
name += " name"
|
name += " name"
|
||||||
|
} else if domain.Extension == "dk" {
|
||||||
|
name = "registrant " + name
|
||||||
} else {
|
} else {
|
||||||
name += " organization"
|
name += " organization"
|
||||||
}
|
}
|
||||||
@ -240,18 +245,21 @@ func parseContact(contact *Contact, name, value string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var searchDomainRx1 = regexp.MustCompile(`(?i)\[?domain\:?(\s*\_?name)?\]?[\s\.]*\:?` +
|
||||||
|
`\s*([^\s\,\;\@\(\)]+)\.([^\s\,\;\(\)\.]{2,})`)
|
||||||
|
var searchDomainRx2 = regexp.MustCompile(`(?i)\[?domain\:?(\s*\_?name)?\]?[\s\.]*\:?` +
|
||||||
|
`\s*([^\s\,\;\@\(\)\.]{2,})\n`)
|
||||||
|
|
||||||
// searchDomain finds domain name and extension from whois information
|
// searchDomain finds domain name and extension from whois information
|
||||||
func searchDomain(text string) (name, extension string) {
|
func searchDomain(text string) (name, extension string) {
|
||||||
r := regexp.MustCompile(`(?i)\[?domain\:?(\s*\_?name)?\]?[\s\.]*\:?\s*([^\s\,\;\(\)]+)\.([^\s\,\;\(\)\.]{2,})`)
|
m := searchDomainRx1.FindStringSubmatch(text)
|
||||||
m := r.FindStringSubmatch(text)
|
|
||||||
if len(m) > 0 {
|
if len(m) > 0 {
|
||||||
name = strings.TrimSpace(m[2])
|
name = strings.TrimPrefix(strings.TrimSpace(m[2]), "\"")
|
||||||
extension = strings.TrimSpace(m[3])
|
extension = strings.TrimSuffix(strings.TrimSpace(m[3]), "\"")
|
||||||
}
|
}
|
||||||
|
|
||||||
if name == "" {
|
if name == "" {
|
||||||
r := regexp.MustCompile(`(?i)\[?domain\:?(\s*\_?name)?\]?[\s\.]*\:?\s*([^\s\,\;\(\)\.]{2,})\n`)
|
m := searchDomainRx2.FindStringSubmatch(text)
|
||||||
m := r.FindStringSubmatch(text)
|
|
||||||
if len(m) > 0 {
|
if len(m) > 0 {
|
||||||
name = strings.TrimSpace(m[2])
|
name = strings.TrimSpace(m[2])
|
||||||
extension = ""
|
extension = ""
|
||||||
|
141
vendor/github.com/likexian/whois-parser/prepare.go
generated
vendored
141
vendor/github.com/likexian/whois-parser/prepare.go
generated
vendored
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2014-2022 Li Kexian
|
* Copyright 2014-2024 Li Kexian
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -91,6 +91,8 @@ func Prepare(text, ext string) (string, bool) { //nolint:cyclop
|
|||||||
return prepareBY(text), true
|
return prepareBY(text), true
|
||||||
case "ua":
|
case "ua":
|
||||||
return prepareUA(text), true
|
return prepareUA(text), true
|
||||||
|
case "at":
|
||||||
|
return prepareAT(text), true
|
||||||
default:
|
default:
|
||||||
return text, false
|
return text, false
|
||||||
}
|
}
|
||||||
@ -329,6 +331,8 @@ func prepareMO(text string) string {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var prepareHKEmailRx = regexp.MustCompile(`Email\:\s+([^\s]+)(\s+Hotline\:(.*))?`)
|
||||||
|
|
||||||
// prepareHK do prepare the .hk domain
|
// prepareHK do prepare the .hk domain
|
||||||
func prepareHK(text string) string {
|
func prepareHK(text string) string {
|
||||||
tokens := map[string]string{
|
tokens := map[string]string{
|
||||||
@ -364,8 +368,7 @@ func prepareHK(text string) string {
|
|||||||
}
|
}
|
||||||
addressToken = field == "Address"
|
addressToken = field == "Address"
|
||||||
if field == "Registrar Contact Information" {
|
if field == "Registrar Contact Information" {
|
||||||
re := regexp.MustCompile(`Email\:\s+([^\s]+)(\s+Hotline\:(.*))?`)
|
m := prepareHKEmailRx.FindStringSubmatch(vs[1])
|
||||||
m := re.FindStringSubmatch(vs[1])
|
|
||||||
if len(m) == 4 {
|
if len(m) == 4 {
|
||||||
v = ""
|
v = ""
|
||||||
if m[1] != "" {
|
if m[1] != "" {
|
||||||
@ -403,6 +406,8 @@ func prepareHK(text string) string {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var prepareTWEmailRx = regexp.MustCompile(`(.*)\s+([^\s]+@[^\s]+)`)
|
||||||
|
|
||||||
// prepareTW do prepare the .tw domain
|
// prepareTW do prepare the .tw domain
|
||||||
func prepareTW(text string) string { //nolint:cyclop
|
func prepareTW(text string) string { //nolint:cyclop
|
||||||
tokens := map[string][]string{
|
tokens := map[string][]string{
|
||||||
@ -489,8 +494,7 @@ func prepareTW(text string) string { //nolint:cyclop
|
|||||||
}
|
}
|
||||||
if strings.Contains(indexName, ",") {
|
if strings.Contains(indexName, ",") {
|
||||||
ins := strings.Split(indexName, ",")
|
ins := strings.Split(indexName, ",")
|
||||||
re := regexp.MustCompile(`(.*)\s+([^\s]+@[^\s]+)`)
|
m := prepareTWEmailRx.FindStringSubmatch(v)
|
||||||
m := re.FindStringSubmatch(v)
|
|
||||||
if len(m) == 3 {
|
if len(m) == 3 {
|
||||||
result += fmt.Sprintf("\n%s %s: %s", tokenName, ins[0], strings.TrimSpace(m[1]))
|
result += fmt.Sprintf("\n%s %s: %s", tokenName, ins[0], strings.TrimSpace(m[1]))
|
||||||
result += fmt.Sprintf("\n%s %s: %s", tokenName, ins[1], strings.TrimSpace(m[2]))
|
result += fmt.Sprintf("\n%s %s: %s", tokenName, ins[1], strings.TrimSpace(m[2]))
|
||||||
@ -683,10 +687,11 @@ func prepareRU(text string) string {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var prepareJPreplacerRx = regexp.MustCompile(`\n(?:\w+\.\s)?\[(.+?)\][\ ]*(.+?)?`)
|
||||||
|
|
||||||
// prepareJP do prepare the .jp domain
|
// prepareJP do prepare the .jp domain
|
||||||
func prepareJP(text string) string {
|
func prepareJP(text string) string {
|
||||||
replacer := regexp.MustCompile(`\n\[(.+?)\][\ ]*(.+?)?`)
|
text = prepareJPreplacerRx.ReplaceAllString(text, "\n$1: $2")
|
||||||
text = replacer.ReplaceAllString(text, "\n$1: $2")
|
|
||||||
|
|
||||||
adminToken := "Contact Information"
|
adminToken := "Contact Information"
|
||||||
addressToken := "Postal Address"
|
addressToken := "Postal Address"
|
||||||
@ -709,6 +714,7 @@ func prepareJP(text string) string {
|
|||||||
if strings.ToLower(token) == "registrant" {
|
if strings.ToLower(token) == "registrant" {
|
||||||
v = fmt.Sprintf("registrant name: %s", vs[1])
|
v = fmt.Sprintf("registrant name: %s", vs[1])
|
||||||
}
|
}
|
||||||
|
v = prepareSecondLevelJP(v, token, vs[1])
|
||||||
} else {
|
} else {
|
||||||
if token == addressToken {
|
if token == addressToken {
|
||||||
result += ", " + v
|
result += ", " + v
|
||||||
@ -721,6 +727,29 @@ func prepareJP(text string) string {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// prepareJP prepares specific mappings for second level .jp domains
|
||||||
|
// examples include:
|
||||||
|
// - co.jp
|
||||||
|
// - ac.jp
|
||||||
|
// - go.jp
|
||||||
|
// - or.jp
|
||||||
|
// - ad.jp
|
||||||
|
// - ne.jp
|
||||||
|
// - gr.jp
|
||||||
|
// - ed.jp
|
||||||
|
func prepareSecondLevelJP(original string, token string, value string) string {
|
||||||
|
if strings.ToLower(token) == "administrative contact" {
|
||||||
|
return fmt.Sprintf("Administrative Contact ID: %s", strings.TrimSpace(value))
|
||||||
|
}
|
||||||
|
if strings.ToLower(token) == "technical contact" {
|
||||||
|
return fmt.Sprintf("Technical Contact ID: %s", strings.TrimSpace(value))
|
||||||
|
}
|
||||||
|
if strings.ToLower(token) == "organization" || strings.ToLower(token) == "network service name" {
|
||||||
|
return fmt.Sprintf("Registrant Organization: %s", strings.TrimSpace(value))
|
||||||
|
}
|
||||||
|
return original
|
||||||
|
}
|
||||||
|
|
||||||
// prepareUK do prepare the .uk domain
|
// prepareUK do prepare the .uk domain
|
||||||
func prepareUK(text string) string {
|
func prepareUK(text string) string {
|
||||||
tokens := map[string]string{
|
tokens := map[string]string{
|
||||||
@ -926,16 +955,15 @@ func prepareEU(text string) string {
|
|||||||
if _, ok := tokens[v]; ok {
|
if _, ok := tokens[v]; ok {
|
||||||
token = tokens[v]
|
token = tokens[v]
|
||||||
continue
|
continue
|
||||||
} else {
|
}
|
||||||
if token != "" {
|
if token != "" {
|
||||||
if strings.Contains(v, ":") {
|
if strings.Contains(v, ":") {
|
||||||
v = fmt.Sprintf("%s %s", token, v)
|
v = fmt.Sprintf("%s %s", token, v)
|
||||||
} else {
|
} else {
|
||||||
if strings.HasPrefix(v, "Visit www.eurid.eu") {
|
if strings.HasPrefix(v, "Visit www.eurid.eu") {
|
||||||
continue
|
continue
|
||||||
}
|
|
||||||
v = fmt.Sprintf("%s: %s", token, v)
|
|
||||||
}
|
}
|
||||||
|
v = fmt.Sprintf("%s: %s", token, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result += "\n" + v
|
result += "\n" + v
|
||||||
@ -1152,9 +1180,8 @@ func prepareEE(text string) string {
|
|||||||
if t, ok := tokens[v]; ok {
|
if t, ok := tokens[v]; ok {
|
||||||
token = t
|
token = t
|
||||||
continue
|
continue
|
||||||
} else {
|
|
||||||
v = fmt.Sprintf("%s %s", token, v)
|
|
||||||
}
|
}
|
||||||
|
v = fmt.Sprintf("%s %s", token, v)
|
||||||
result += "\n" + strings.TrimSpace(v)
|
result += "\n" + strings.TrimSpace(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1191,9 +1218,8 @@ func preparePL(text string) string {
|
|||||||
ns := strings.SplitN(v, "[", 2)
|
ns := strings.SplitN(v, "[", 2)
|
||||||
result += fmt.Sprintf("\nnameservers: %s", strings.TrimSpace(ns[0]))
|
result += fmt.Sprintf("\nnameservers: %s", strings.TrimSpace(ns[0]))
|
||||||
continue
|
continue
|
||||||
} else {
|
|
||||||
special = ""
|
|
||||||
}
|
}
|
||||||
|
special = ""
|
||||||
} else if special == "REGISTRAR" {
|
} else if special == "REGISTRAR" {
|
||||||
if strings.TrimSpace(v) == "" {
|
if strings.TrimSpace(v) == "" {
|
||||||
special = ""
|
special = ""
|
||||||
@ -1335,3 +1361,78 @@ func prepareUA(text string) string {
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// prepareAT prepares the .at domain
|
||||||
|
func prepareAT(text string) string {
|
||||||
|
result := ""
|
||||||
|
registrantID := ""
|
||||||
|
techID := ""
|
||||||
|
|
||||||
|
tokens := map[string]string{
|
||||||
|
"street address": "address",
|
||||||
|
"postal code": "address",
|
||||||
|
"city": "address",
|
||||||
|
"country": "address",
|
||||||
|
"e-mail": "email",
|
||||||
|
"nic-hdl": "id",
|
||||||
|
"personname": "name",
|
||||||
|
}
|
||||||
|
|
||||||
|
formatLine := func(line, token string) string {
|
||||||
|
before, after, _ := strings.Cut(line, ":")
|
||||||
|
key := strings.TrimSpace(before)
|
||||||
|
if t, ok := tokens[key]; ok {
|
||||||
|
key = t
|
||||||
|
}
|
||||||
|
val := strings.TrimSpace(after)
|
||||||
|
return fmt.Sprintf("%s %s: %s", token, key, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range strings.Split(text, "\n\n") {
|
||||||
|
v = strings.TrimSpace(v)
|
||||||
|
if strings.HasPrefix(v, "%") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.Contains(v, ":") {
|
||||||
|
b := strings.Split(v, "\n")
|
||||||
|
if strings.HasPrefix(b[0], "domain") {
|
||||||
|
for _, l := range b {
|
||||||
|
w := ""
|
||||||
|
if before, after, ok := strings.Cut(l, ":"); ok {
|
||||||
|
key := strings.TrimSpace(before)
|
||||||
|
val := strings.TrimSpace(after)
|
||||||
|
switch key {
|
||||||
|
case "domain":
|
||||||
|
w = fmt.Sprintf("%s: %s", "domain name", val)
|
||||||
|
case "registrant":
|
||||||
|
registrantID = val
|
||||||
|
case "tech-c":
|
||||||
|
techID = val
|
||||||
|
case "changed":
|
||||||
|
w = fmt.Sprintf("%s: %s", "updated_date", val)
|
||||||
|
case "nserver":
|
||||||
|
w = fmt.Sprintf("%s: %s", "name_servers", val)
|
||||||
|
default:
|
||||||
|
w = fmt.Sprintf("domain %s: %s", key, val)
|
||||||
|
}
|
||||||
|
if w != "" {
|
||||||
|
result += w + "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if strings.HasPrefix(b[0], "personname") {
|
||||||
|
token := ""
|
||||||
|
if strings.Contains(v, registrantID) {
|
||||||
|
token = "registrant"
|
||||||
|
} else if strings.Contains(v, techID) {
|
||||||
|
token = "technical contact"
|
||||||
|
}
|
||||||
|
for _, l := range strings.Split(v, "\n") {
|
||||||
|
result += formatLine(l, token) + "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
5
vendor/github.com/likexian/whois-parser/rule.go
generated
vendored
5
vendor/github.com/likexian/whois-parser/rule.go
generated
vendored
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2014-2022 Li Kexian
|
* Copyright 2014-2024 Li Kexian
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -68,6 +68,7 @@ var (
|
|||||||
"registration time": "created_date",
|
"registration time": "created_date",
|
||||||
"first registration date": "created_date",
|
"first registration date": "created_date",
|
||||||
"domain record activated": "created_date",
|
"domain record activated": "created_date",
|
||||||
|
"record created": "created_date",
|
||||||
"record created on": "created_date",
|
"record created on": "created_date",
|
||||||
"domain registered": "created_date",
|
"domain registered": "created_date",
|
||||||
"modified": "updated_date",
|
"modified": "updated_date",
|
||||||
@ -151,7 +152,9 @@ var (
|
|||||||
"registrant contact state province": "registrant_state_province",
|
"registrant contact state province": "registrant_state_province",
|
||||||
"registrant zipcode": "registrant_postal_code",
|
"registrant zipcode": "registrant_postal_code",
|
||||||
"registrant zip code": "registrant_postal_code",
|
"registrant zip code": "registrant_postal_code",
|
||||||
|
"registrant postalcode": "registrant_postal_code",
|
||||||
"registrant postal code": "registrant_postal_code",
|
"registrant postal code": "registrant_postal_code",
|
||||||
|
"registrant contact postalcode": "registrant_postal_code",
|
||||||
"registrant contact postal code": "registrant_postal_code",
|
"registrant contact postal code": "registrant_postal_code",
|
||||||
"registrant country": "registrant_country",
|
"registrant country": "registrant_country",
|
||||||
"registrant country economy": "registrant_country",
|
"registrant country economy": "registrant_country",
|
||||||
|
2
vendor/github.com/likexian/whois-parser/struct.go
generated
vendored
2
vendor/github.com/likexian/whois-parser/struct.go
generated
vendored
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2014-2022 Li Kexian
|
* Copyright 2014-2024 Li Kexian
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
22
vendor/github.com/likexian/whois-parser/util.go
generated
vendored
22
vendor/github.com/likexian/whois-parser/util.go
generated
vendored
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2014-2022 Li Kexian
|
* Copyright 2014-2024 Li Kexian
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -73,6 +73,9 @@ func fixDomainStatus(status []string) []string {
|
|||||||
for k, v := range status {
|
for k, v := range status {
|
||||||
names := strings.Split(strings.TrimSpace(v), " ")
|
names := strings.Split(strings.TrimSpace(v), " ")
|
||||||
status[k] = strings.ToLower(names[0])
|
status[k] = strings.ToLower(names[0])
|
||||||
|
if status[k] == "not" && len(names) > 1 && strings.ToLower(names[1]) == "delegated" {
|
||||||
|
status[k] = "not delegated"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return status
|
return status
|
||||||
@ -115,16 +118,20 @@ func keys(m map[string]string) []string {
|
|||||||
// parseDateString attempts to parse a given date using a collection of common
|
// parseDateString attempts to parse a given date using a collection of common
|
||||||
// format strings. Date formats containing time components are tried first
|
// format strings. Date formats containing time components are tried first
|
||||||
// before attempts are made using date-only formats.
|
// before attempts are made using date-only formats.
|
||||||
func parseDateString(dateString string) (time.Time, error) {
|
func parseDateString(datetime string) (time.Time, error) {
|
||||||
formats := [...]string{
|
datetime = strings.Trim(datetime, ".")
|
||||||
|
datetime = strings.ReplaceAll(datetime, ". ", "-")
|
||||||
|
|
||||||
|
formats := [...]string{
|
||||||
// Date & time formats
|
// Date & time formats
|
||||||
"2006-01-02 15:04:05",
|
"2006-01-02 15:04:05",
|
||||||
|
"2006.01.02 15:04:05",
|
||||||
"02/01/2006 15:04:05",
|
"02/01/2006 15:04:05",
|
||||||
"02.01.2006 15:04:05",
|
"02.01.2006 15:04:05",
|
||||||
"02.1.2006 15:04:05",
|
"02.1.2006 15:04:05",
|
||||||
"2.1.2006 15:04:05",
|
"2.1.2006 15:04:05",
|
||||||
"02-Jan-2006 15:04:05",
|
"02-Jan-2006 15:04:05",
|
||||||
|
"20060102 15:04:05",
|
||||||
time.ANSIC,
|
time.ANSIC,
|
||||||
time.Stamp,
|
time.Stamp,
|
||||||
time.StampMilli,
|
time.StampMilli,
|
||||||
@ -148,24 +155,25 @@ func parseDateString(dateString string) (time.Time, error) {
|
|||||||
|
|
||||||
// Date only formats
|
// Date only formats
|
||||||
"2006-01-02",
|
"2006-01-02",
|
||||||
"2006. 01. 02.",
|
|
||||||
"02-Jan-2006",
|
"02-Jan-2006",
|
||||||
"02.01.2006",
|
"02.01.2006",
|
||||||
"02-01-2006",
|
"02-01-2006",
|
||||||
"January _2 2006",
|
"January _2 2006",
|
||||||
|
"Mon Jan _2 2006",
|
||||||
"02/01/2006",
|
"02/01/2006",
|
||||||
"01/02/2006",
|
"01/02/2006",
|
||||||
|
"2006/01/02",
|
||||||
"2006-Jan-02",
|
"2006-Jan-02",
|
||||||
"2006-Jan-02.",
|
"before Jan-2006",
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, format := range formats {
|
for _, format := range formats {
|
||||||
result, err := time.Parse(format, dateString)
|
result, err := time.Parse(format, datetime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return time.Now(), fmt.Errorf("could not parse %s as a date", dateString)
|
return time.Now(), fmt.Errorf("could not parse %s as a date", datetime)
|
||||||
}
|
}
|
||||||
|
1
vendor/github.com/likexian/whois/.gitignore
generated
vendored
1
vendor/github.com/likexian/whois/.gitignore
generated
vendored
@ -1 +1,2 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
|
.idea
|
||||||
|
3
vendor/github.com/likexian/whois/.golangci.yml
generated
vendored
3
vendor/github.com/likexian/whois/.golangci.yml
generated
vendored
@ -9,16 +9,13 @@ run:
|
|||||||
linters:
|
linters:
|
||||||
disable-all: true
|
disable-all: true
|
||||||
enable:
|
enable:
|
||||||
- deadcode
|
|
||||||
- errcheck
|
- errcheck
|
||||||
- gosimple
|
- gosimple
|
||||||
- govet
|
- govet
|
||||||
- ineffassign
|
- ineffassign
|
||||||
- staticcheck
|
- staticcheck
|
||||||
- structcheck
|
|
||||||
- typecheck
|
- typecheck
|
||||||
- unused
|
- unused
|
||||||
- varcheck
|
|
||||||
- cyclop
|
- cyclop
|
||||||
- durationcheck
|
- durationcheck
|
||||||
- errname
|
- errname
|
||||||
|
4
vendor/github.com/likexian/whois/LICENSE
generated
vendored
4
vendor/github.com/likexian/whois/LICENSE
generated
vendored
@ -186,7 +186,7 @@
|
|||||||
same "printed page" as the copyright notice for easier
|
same "printed page" as the copyright notice for easier
|
||||||
identification within third-party archives.
|
identification within third-party archives.
|
||||||
|
|
||||||
Copyright 2014-2022 Li Kexian
|
Copyright 2014-2024 Li Kexian
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -200,6 +200,6 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|
||||||
APPENDIX: Copyright 2014-2022 Li Kexian
|
APPENDIX: Copyright 2014-2024 Li Kexian
|
||||||
|
|
||||||
https://www.likexian.com/
|
https://www.likexian.com/
|
||||||
|
2
vendor/github.com/likexian/whois/README.md
generated
vendored
2
vendor/github.com/likexian/whois/README.md
generated
vendored
@ -79,7 +79,7 @@ Please refer to [whois-parser](https://github.com/likexian/whois-parser)
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Copyright 2014-2022 [Li Kexian](https://www.likexian.com/)
|
Copyright 2014-2024 [Li Kexian](https://www.likexian.com/)
|
||||||
|
|
||||||
Licensed under the Apache License 2.0
|
Licensed under the Apache License 2.0
|
||||||
|
|
||||||
|
2
vendor/github.com/likexian/whois/error.go
generated
vendored
2
vendor/github.com/likexian/whois/error.go
generated
vendored
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2014-2022 Li Kexian
|
* Copyright 2014-2024 Li Kexian
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
56
vendor/github.com/likexian/whois/whois.go
generated
vendored
56
vendor/github.com/likexian/whois/whois.go
generated
vendored
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2014-2022 Li Kexian
|
* Copyright 2014-2024 Li Kexian
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -21,7 +21,7 @@ package whois
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -46,14 +46,16 @@ var DefaultClient = NewClient()
|
|||||||
|
|
||||||
// Client is whois client
|
// Client is whois client
|
||||||
type Client struct {
|
type Client struct {
|
||||||
dialer proxy.Dialer
|
dialer proxy.Dialer
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
elapsed time.Duration
|
elapsed time.Duration
|
||||||
|
disableStats bool
|
||||||
|
disableReferral bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Version returns package version
|
// Version returns package version
|
||||||
func Version() string {
|
func Version() string {
|
||||||
return "1.14.4"
|
return "1.15.4"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Author returns package author
|
// Author returns package author
|
||||||
@ -82,24 +84,39 @@ func NewClient() *Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetDialer set query net dialer
|
// SetDialer set query net dialer
|
||||||
func (c *Client) SetDialer(dialer proxy.Dialer) {
|
func (c *Client) SetDialer(dialer proxy.Dialer) *Client {
|
||||||
c.dialer = dialer
|
c.dialer = dialer
|
||||||
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTimeout set query timeout
|
// SetTimeout set query timeout
|
||||||
func (c *Client) SetTimeout(timeout time.Duration) {
|
func (c *Client) SetTimeout(timeout time.Duration) *Client {
|
||||||
c.timeout = timeout
|
c.timeout = timeout
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDisableStats set disable stats
|
||||||
|
func (c *Client) SetDisableStats(disabled bool) *Client {
|
||||||
|
c.disableStats = disabled
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDisableReferral if set to true, will not query the referral server.
|
||||||
|
func (c *Client) SetDisableReferral(disabled bool) *Client {
|
||||||
|
c.disableReferral = disabled
|
||||||
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whois do the whois query and returns whois information
|
// Whois do the whois query and returns whois information
|
||||||
func (c *Client) Whois(domain string, servers ...string) (result string, err error) {
|
func (c *Client) Whois(domain string, servers ...string) (result string, err error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
defer func() {
|
defer func() {
|
||||||
result = fmt.Sprintf("%s\n\n;; Query time: %d msec\n;; WHEN: %s\n",
|
result = strings.TrimSpace(result)
|
||||||
strings.TrimRight(result, "\n"),
|
if result != "" && !c.disableStats {
|
||||||
time.Since(start).Milliseconds(),
|
result = fmt.Sprintf("%s\n\n%% Query time: %d msec\n%% WHEN: %s\n",
|
||||||
start.Format("Mon Jan 02 15:04:05 MST 2006"),
|
result, time.Since(start).Milliseconds(), start.Format("Mon Jan 02 15:04:05 MST 2006"),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
domain = strings.Trim(strings.TrimSpace(domain), ".")
|
domain = strings.Trim(strings.TrimSpace(domain), ".")
|
||||||
@ -139,6 +156,10 @@ func (c *Client) Whois(domain string, servers ...string) (result string, err err
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.disableReferral {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
refServer, refPort := getServer(result)
|
refServer, refPort := getServer(result)
|
||||||
if refServer == "" || refServer == server {
|
if refServer == "" || refServer == server {
|
||||||
return
|
return
|
||||||
@ -170,6 +191,11 @@ func (c *Client) rawQuery(domain, server, port string) (string, error) {
|
|||||||
server = "whois.godaddy.com"
|
server = "whois.godaddy.com"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See: https://github.com/likexian/whois/pull/30
|
||||||
|
if server == "porkbun.com/whois" {
|
||||||
|
server = "whois.porkbun.com"
|
||||||
|
}
|
||||||
|
|
||||||
conn, err := c.dialer.Dial("tcp", net.JoinHostPort(server, port))
|
conn, err := c.dialer.Dial("tcp", net.JoinHostPort(server, port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("whois: connect to whois server failed: %w", err)
|
return "", fmt.Errorf("whois: connect to whois server failed: %w", err)
|
||||||
@ -187,7 +213,7 @@ func (c *Client) rawQuery(domain, server, port string) (string, error) {
|
|||||||
c.elapsed = time.Since(start)
|
c.elapsed = time.Since(start)
|
||||||
|
|
||||||
_ = conn.SetReadDeadline(time.Now().Add(c.timeout - c.elapsed))
|
_ = conn.SetReadDeadline(time.Now().Add(c.timeout - c.elapsed))
|
||||||
buffer, err := ioutil.ReadAll(conn)
|
buffer, err := io.ReadAll(conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("whois: read from whois server failed: %w", err)
|
return "", fmt.Errorf("whois: read from whois server failed: %w", err)
|
||||||
}
|
}
|
||||||
@ -219,6 +245,7 @@ func getServer(data string) (string, string) {
|
|||||||
"Registrar WHOIS Server: ",
|
"Registrar WHOIS Server: ",
|
||||||
"whois: ",
|
"whois: ",
|
||||||
"ReferralServer: ",
|
"ReferralServer: ",
|
||||||
|
"refer: ",
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, token := range tokens {
|
for _, token := range tokens {
|
||||||
@ -228,6 +255,7 @@ func getServer(data string) (string, string) {
|
|||||||
end := strings.Index(data[start:], "\n")
|
end := strings.Index(data[start:], "\n")
|
||||||
server := strings.TrimSpace(data[start : start+end])
|
server := strings.TrimSpace(data[start : start+end])
|
||||||
server = strings.TrimPrefix(server, "http:")
|
server = strings.TrimPrefix(server, "http:")
|
||||||
|
server = strings.TrimPrefix(server, "https:")
|
||||||
server = strings.TrimPrefix(server, "whois:")
|
server = strings.TrimPrefix(server, "whois:")
|
||||||
server = strings.TrimPrefix(server, "rwhois:")
|
server = strings.TrimPrefix(server, "rwhois:")
|
||||||
server = strings.Trim(server, "/")
|
server = strings.Trim(server, "/")
|
||||||
|
3
vendor/github.com/mattn/go-isatty/isatty_bsd.go
generated
vendored
3
vendor/github.com/mattn/go-isatty/isatty_bsd.go
generated
vendored
@ -1,6 +1,7 @@
|
|||||||
//go:build (darwin || freebsd || openbsd || netbsd || dragonfly || hurd) && !appengine
|
//go:build (darwin || freebsd || openbsd || netbsd || dragonfly || hurd) && !appengine && !tinygo
|
||||||
// +build darwin freebsd openbsd netbsd dragonfly hurd
|
// +build darwin freebsd openbsd netbsd dragonfly hurd
|
||||||
// +build !appengine
|
// +build !appengine
|
||||||
|
// +build !tinygo
|
||||||
|
|
||||||
package isatty
|
package isatty
|
||||||
|
|
||||||
|
5
vendor/github.com/mattn/go-isatty/isatty_others.go
generated
vendored
5
vendor/github.com/mattn/go-isatty/isatty_others.go
generated
vendored
@ -1,5 +1,6 @@
|
|||||||
//go:build appengine || js || nacl || wasm
|
//go:build (appengine || js || nacl || tinygo || wasm) && !windows
|
||||||
// +build appengine js nacl wasm
|
// +build appengine js nacl tinygo wasm
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
package isatty
|
package isatty
|
||||||
|
|
||||||
|
3
vendor/github.com/mattn/go-isatty/isatty_tcgets.go
generated
vendored
3
vendor/github.com/mattn/go-isatty/isatty_tcgets.go
generated
vendored
@ -1,6 +1,7 @@
|
|||||||
//go:build (linux || aix || zos) && !appengine
|
//go:build (linux || aix || zos) && !appengine && !tinygo
|
||||||
// +build linux aix zos
|
// +build linux aix zos
|
||||||
// +build !appengine
|
// +build !appengine
|
||||||
|
// +build !tinygo
|
||||||
|
|
||||||
package isatty
|
package isatty
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user