updated vmail backend
This commit is contained in:
parent
6e5ee16178
commit
40f2487786
18
go.mod
18
go.mod
@ -1,11 +1,19 @@
|
|||||||
module git.paulbsd.com/paulbsd/vmail
|
module git.paulbsd.com/paulbsd/vmail
|
||||||
|
|
||||||
go 1.14
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/labstack/echo/v4 v4.1.16
|
github.com/golang/snappy v0.0.3 // indirect
|
||||||
github.com/lib/pq v1.8.0
|
github.com/labstack/echo/v4 v4.2.1
|
||||||
|
github.com/lib/pq v1.10.0
|
||||||
|
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||||
github.com/smartystreets/goconvey v1.6.4 // indirect
|
github.com/smartystreets/goconvey v1.6.4 // indirect
|
||||||
gopkg.in/ini.v1 v1.57.0
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect
|
||||||
xorm.io/xorm v1.0.3
|
golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 // indirect
|
||||||
|
golang.org/x/text v0.3.6 // indirect
|
||||||
|
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||||
|
gopkg.in/ini.v1 v1.62.0
|
||||||
|
xorm.io/builder v0.3.9 // indirect
|
||||||
|
xorm.io/xorm v1.0.7
|
||||||
)
|
)
|
||||||
|
66
go.sum
66
go.sum
@ -1,9 +1,12 @@
|
|||||||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
|
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
|
||||||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
|
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
|
||||||
|
github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=
|
||||||
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||||
|
github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo=
|
||||||
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc h1:VRRKCwnzqk8QCaRC4os14xoKDdbHqqlJtJA0oc1ZAjg=
|
||||||
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
@ -11,27 +14,30 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV
|
|||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
|
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
|
||||||
|
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/labstack/echo/v4 v4.1.16 h1:8swiwjE5Jkai3RPfZoahp8kjVCRNq+y7Q0hPji2Kz0o=
|
github.com/labstack/echo/v4 v4.2.1 h1:LF5Iq7t/jrtUuSutNuiEWtB5eiHfZ5gSe2pcu5exjQw=
|
||||||
github.com/labstack/echo/v4 v4.1.16/go.mod h1:awO+5TzAjvL8XpibdsfXxPgHr+orhtXZJZIQCVjogKI=
|
github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
|
||||||
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
|
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
|
||||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
||||||
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg=
|
github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E=
|
||||||
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
|
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||||
|
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||||
@ -49,6 +55,7 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykE
|
|||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
|
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
@ -58,21 +65,26 @@ github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpP
|
|||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||||
github.com/valyala/fasttemplate v1.1.0 h1:RZqt0yGBsps8NGvLSGW804QQqCUYYLsaOjTVHy1Ocw4=
|
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
|
||||||
github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||||
|
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
||||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAakHVhecoE5wlSg5GjnafJGw=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
|
||||||
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
|
|
||||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c h1:KHUzaHIpjWVlVVNh65G3hhuj3KB1HnjY6Cq5cTvRQT8=
|
||||||
|
golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@ -82,25 +94,37 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 h1:F5Gozwx4I1xtr/sr/8CFbb57iKi3297KFs0QDbGN60A=
|
||||||
|
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
|
||||||
|
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384 h1:TFlARGu6Czu1z7q93HTxcP1P+/ZFC/IKythI5RzrnRg=
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww=
|
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
||||||
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
xorm.io/builder v0.3.7 h1:2pETdKRK+2QG4mLX4oODHEhn5Z8j1m8sXa7jfu+/SZI=
|
|
||||||
xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
|
xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
|
||||||
xorm.io/xorm v1.0.3 h1:3dALAohvINu2mfEix5a5x5ZmSVGSljinoSGgvGbaZp0=
|
xorm.io/builder v0.3.9 h1:Sd65/LdWyO7LR8+Cbd+e7mm3sK/7U9k0jS3999IDHMc=
|
||||||
xorm.io/xorm v1.0.3/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4=
|
xorm.io/builder v0.3.9/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
|
||||||
|
xorm.io/xorm v1.0.7 h1:26yBTDVI+CfQpVz2Y88fISh+aiJXIPP4eNoTJlwzsC4=
|
||||||
|
xorm.io/xorm v1.0.7/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4=
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
// Admin defines the admin struct
|
// Admin defines the admin struct for api
|
||||||
type Admin struct {
|
type Admin struct {
|
||||||
|
ID int `json:"id"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Created string `json:"created"`
|
Created string `json:"created"`
|
||||||
Modified string `json:"modified"`
|
Modified string `json:"modified"`
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
// Alias defines the admin struct
|
// Alias defines the alias struct for api
|
||||||
type Alias struct {
|
type Alias struct {
|
||||||
|
ID int `json:"id"`
|
||||||
Address string `json:"address"`
|
Address string `json:"address"`
|
||||||
Goto string `json:"goto"`
|
Goto string `json:"goto"`
|
||||||
Domain string `json:"domain"`
|
Domain string `json:"domain"`
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
// Log defines the admin struct
|
// Log defines the log struct for api
|
||||||
type Log struct {
|
type Log struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
Timestamp string `json:"timestamp"`
|
Timestamp string `json:"timestamp"`
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
// Mailbox defines the admin struct
|
// Mailbox defines the mailbox struct for api
|
||||||
type Mailbox struct {
|
type Mailbox struct {
|
||||||
|
ID int `json:"id"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"git.paulbsd.com/paulbsd/vmail/src/models"
|
"git.paulbsd.com/paulbsd/vmail/src/models"
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
"xorm.io/xorm/dialects"
|
||||||
"xorm.io/xorm/names"
|
"xorm.io/xorm/names"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -20,7 +21,7 @@ func Initialize(ctx *context.Context, config *config.Config) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
config.Db.SetMapper(names.GonicMapper{})
|
config.Db.SetMapper(names.GonicMapper{})
|
||||||
config.Db.SetQuotePolicy(2)
|
config.Db.SetQuotePolicy(dialects.QuotePolicyReserved)
|
||||||
|
|
||||||
if config.Debug {
|
if config.Debug {
|
||||||
config.Db.ShowSQL(true)
|
config.Db.ShowSQL(true)
|
||||||
|
@ -2,10 +2,12 @@ package models
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.paulbsd.com/paulbsd/vmail/src/api"
|
"git.paulbsd.com/paulbsd/vmail/src/api"
|
||||||
"git.paulbsd.com/paulbsd/vmail/src/config"
|
"git.paulbsd.com/paulbsd/vmail/src/config"
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetAdmins return list of apiadmins
|
// GetAdmins return list of apiadmins
|
||||||
@ -18,10 +20,17 @@ func GetAdmins(ctx *context.Context, config *config.Config) (apiadmins []*api.Ad
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAdminModel ...
|
||||||
|
func GetAdminModel(ctx *context.Context, config *config.Config) (apiadmin *api.Admin, err error) {
|
||||||
|
var admin Admin
|
||||||
|
apiadmin = admin.APIFormat()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// GetAdmin ...
|
// GetAdmin ...
|
||||||
func GetAdmin(ctx *context.Context, config *config.Config, id interface{}) (apiadmin *api.Admin, err error) {
|
func GetAdmin(ctx *context.Context, config *config.Config, id interface{}) (apiadmin *api.Admin, err error) {
|
||||||
var admin Admin
|
var admin Admin
|
||||||
has, err := config.Db.Where("username = ?", id).Get(&admin)
|
has, err := config.Db.Where(fmt.Sprintf("%s = ?", keyname), id).Get(&admin)
|
||||||
if !has || err != nil {
|
if !has || err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -29,12 +38,59 @@ func GetAdmin(ctx *context.Context, config *config.Config, id interface{}) (apia
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateAdmin ...
|
||||||
|
func CreateAdmin(ctx *context.Context, config *config.Config) (num int64, err error) {
|
||||||
|
var admin Admin
|
||||||
|
num, err = config.Db.Insert(&admin)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateAdmin ...
|
||||||
|
func UpdateAdmin(ctx *context.Context, config *config.Config, c echo.Context) (num int64, err error) {
|
||||||
|
var admin Admin
|
||||||
|
var apiadmin = new(api.Admin)
|
||||||
|
if err = c.Bind(apiadmin); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
admin.APIParse(*apiadmin)
|
||||||
|
num, err = config.Db.ID(admin.ID).AllCols().Update(&admin)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteAdmin ...
|
||||||
|
func DeleteAdmin(ctx *context.Context, config *config.Config, id interface{}) (num int64, err error) {
|
||||||
|
var alias Alias
|
||||||
|
num, err = config.Db.Where(fmt.Sprintf("%s = ?", keyname), id).Delete(&alias)
|
||||||
|
if num > 0 || err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// APIParse returns a JSON formatted object of Admin
|
||||||
|
func (alias *Admin) APIParse(apiadmin api.Admin) (err error) {
|
||||||
|
*alias = Admin{
|
||||||
|
ID: apiadmin.ID,
|
||||||
|
Username: apiadmin.Username,
|
||||||
|
Active: apiadmin.Active,
|
||||||
|
Superadmin: apiadmin.Superadmin,
|
||||||
|
Phone: apiadmin.Phone,
|
||||||
|
EmailOther: apiadmin.EmailOther,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// APIFormat returns a JSON formatted object of Admin
|
// APIFormat returns a JSON formatted object of Admin
|
||||||
func (admin *Admin) APIFormat() *api.Admin {
|
func (admin *Admin) APIFormat() *api.Admin {
|
||||||
if admin == nil {
|
if admin == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return &api.Admin{
|
return &api.Admin{
|
||||||
|
ID: admin.ID,
|
||||||
Username: admin.Username,
|
Username: admin.Username,
|
||||||
Created: admin.Created.Format(timetostring),
|
Created: admin.Created.Format(timetostring),
|
||||||
Modified: admin.Modified.Format(timetostring),
|
Modified: admin.Modified.Format(timetostring),
|
||||||
@ -47,10 +103,11 @@ func (admin *Admin) APIFormat() *api.Admin {
|
|||||||
|
|
||||||
// Admin defines the admin struct
|
// Admin defines the admin struct
|
||||||
type Admin struct {
|
type Admin struct {
|
||||||
Username string `xorm:"not null pk unique VARCHAR(255)"`
|
ID int `xorm:"not null pk unique serial"`
|
||||||
|
Username string `xorm:"not null unique VARCHAR(255)"`
|
||||||
Password string `xorm:"not null default '' VARCHAR(255)"`
|
Password string `xorm:"not null default '' VARCHAR(255)"`
|
||||||
Created time.Time `xorm:"default now() TIMESTAMPZ"`
|
Created time.Time `xorm:"default now() TIMESTAMPZ created"`
|
||||||
Modified time.Time `xorm:"default now() TIMESTAMPZ"`
|
Modified time.Time `xorm:"default now() TIMESTAMPZ modified"`
|
||||||
Active bool `xorm:"not null default true BOOL"`
|
Active bool `xorm:"not null default true BOOL"`
|
||||||
Superadmin bool `xorm:"not null default false BOOL"`
|
Superadmin bool `xorm:"not null default false BOOL"`
|
||||||
Phone string `xorm:"not null default '' VARCHAR(30)"`
|
Phone string `xorm:"not null default '' VARCHAR(30)"`
|
||||||
|
@ -7,10 +7,9 @@ import (
|
|||||||
|
|
||||||
"git.paulbsd.com/paulbsd/vmail/src/api"
|
"git.paulbsd.com/paulbsd/vmail/src/api"
|
||||||
"git.paulbsd.com/paulbsd/vmail/src/config"
|
"git.paulbsd.com/paulbsd/vmail/src/config"
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
const key = "address"
|
|
||||||
|
|
||||||
// GetAliases ...
|
// GetAliases ...
|
||||||
func GetAliases(ctx *context.Context, config *config.Config) (apialiases []*api.Alias, err error) {
|
func GetAliases(ctx *context.Context, config *config.Config) (apialiases []*api.Alias, err error) {
|
||||||
var aliases []Alias
|
var aliases []Alias
|
||||||
@ -21,10 +20,17 @@ func GetAliases(ctx *context.Context, config *config.Config) (apialiases []*api.
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAliasModel ...
|
||||||
|
func GetAliasModel(ctx *context.Context, config *config.Config) (apialias *api.Alias, err error) {
|
||||||
|
var alias Alias
|
||||||
|
apialias = alias.APIFormat()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// GetAlias ...
|
// GetAlias ...
|
||||||
func GetAlias(ctx *context.Context, config *config.Config, id interface{}) (apialias *api.Alias, err error) {
|
func GetAlias(ctx *context.Context, config *config.Config, id interface{}) (apialias *api.Alias, err error) {
|
||||||
var alias Alias
|
var alias Alias
|
||||||
has, err := config.Db.Where(fmt.Sprintf("%s = ?", key), id).Get(&alias)
|
has, err := config.Db.Where(fmt.Sprintf("%s = ?", keyname), id).Get(&alias)
|
||||||
if !has || err != nil {
|
if !has || err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -43,32 +49,47 @@ func CreateAlias(ctx *context.Context, config *config.Config) (num int64, err er
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateAlias ...
|
// UpdateAlias ...
|
||||||
func UpdateAlias(ctx *context.Context, config *config.Config, id interface{}) (apialias *api.Alias, err error) {
|
func UpdateAlias(ctx *context.Context, config *config.Config, c echo.Context) (num int64, err error) {
|
||||||
var alias Alias
|
var alias Alias
|
||||||
has, err := config.Db.Where(fmt.Sprintf("%s = ?", key), id).Get(&alias)
|
var apialias = new(api.Alias)
|
||||||
if !has || err != nil {
|
if err = c.Bind(apialias); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
apialias = alias.APIFormat()
|
alias.APIParse(*apialias)
|
||||||
|
num, err = config.Db.ID(alias.ID).AllCols().Update(&alias)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteAlias ...
|
// DeleteAlias ...
|
||||||
func DeleteAlias(ctx *context.Context, config *config.Config, id interface{}) (num int64, err error) {
|
func DeleteAlias(ctx *context.Context, config *config.Config, id interface{}) (num int64, err error) {
|
||||||
var alias Alias
|
var alias Alias
|
||||||
num, err = config.Db.Where(fmt.Sprintf("%s = ?", key), id).Delete(&alias)
|
num, err = config.Db.Where(fmt.Sprintf("%s = ?", keyname), id).Delete(&alias)
|
||||||
if num > 0 || err != nil {
|
if num > 0 || err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// APIFormat returns a JSON formatted object of Admin
|
// APIParse returns a JSON formatted object of Alias
|
||||||
|
func (alias *Alias) APIParse(apialias api.Alias) (err error) {
|
||||||
|
*alias = Alias{
|
||||||
|
ID: apialias.ID,
|
||||||
|
Address: apialias.Address,
|
||||||
|
Goto: apialias.Goto,
|
||||||
|
Domain: apialias.Domain,
|
||||||
|
Active: apialias.Active,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// APIFormat returns a JSON formatted object of Alias
|
||||||
func (alias *Alias) APIFormat() *api.Alias {
|
func (alias *Alias) APIFormat() *api.Alias {
|
||||||
if alias == nil {
|
if alias == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return &api.Alias{
|
return &api.Alias{
|
||||||
|
ID: alias.ID,
|
||||||
Address: alias.Address,
|
Address: alias.Address,
|
||||||
Goto: alias.Goto,
|
Goto: alias.Goto,
|
||||||
Domain: alias.Domain,
|
Domain: alias.Domain,
|
||||||
@ -80,10 +101,11 @@ func (alias *Alias) APIFormat() *api.Alias {
|
|||||||
|
|
||||||
// Alias defines the admin struct
|
// Alias defines the admin struct
|
||||||
type Alias struct {
|
type Alias struct {
|
||||||
Address string `xorm:"not null pk index(alias_address_active) unique VARCHAR(255)"`
|
ID int `xorm:"not null pk unique serial"`
|
||||||
|
Address string `xorm:"not null index(alias_address_active) unique VARCHAR(255)"`
|
||||||
Goto string `xorm:"not null TEXT"`
|
Goto string `xorm:"not null TEXT"`
|
||||||
Domain string `xorm:"not null index VARCHAR(255)"`
|
Domain string `xorm:"not null index VARCHAR(255)"`
|
||||||
Created time.Time `xorm:"default now() TIMESTAMPZ"`
|
Created time.Time `xorm:"default now() TIMESTAMPZ created"`
|
||||||
Modified time.Time `xorm:"default now() TIMESTAMPZ"`
|
Modified time.Time `xorm:"default now() TIMESTAMPZ updated"`
|
||||||
Active bool `xorm:"default false index(alias_address_active) BOOL"`
|
Active bool `xorm:"default false index(alias_address_active) BOOL"`
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,10 @@ import "time"
|
|||||||
|
|
||||||
// AliasDomain defines domain aliases
|
// AliasDomain defines domain aliases
|
||||||
type AliasDomain struct {
|
type AliasDomain struct {
|
||||||
|
ID int `xorm:"not null pk unique serial"`
|
||||||
AliasDomain string `xorm:"not null index(alias_domain_active) VARCHAR(255)"`
|
AliasDomain string `xorm:"not null index(alias_domain_active) VARCHAR(255)"`
|
||||||
TargetDomain string `xorm:"not null VARCHAR(255)"`
|
TargetDomain string `xorm:"not null VARCHAR(255)"`
|
||||||
Created time.Time `xorm:"default now() TIMESTAMPZ"`
|
Created time.Time `xorm:"default now() TIMESTAMPZ created"`
|
||||||
Modified time.Time `xorm:"default now() TIMESTAMPZ"`
|
Modified time.Time `xorm:"default now() TIMESTAMPZ modified"`
|
||||||
Active bool `xorm:"not null default true index(alias_domain_active) BOOL"`
|
Active bool `xorm:"not null default true index(alias_domain_active) BOOL"`
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ package models
|
|||||||
|
|
||||||
// Config defines config table
|
// Config defines config table
|
||||||
type Config struct {
|
type Config struct {
|
||||||
ID int `xorm:"not null pk autoincr INTEGER"`
|
ID int `xorm:"not null pk serial"`
|
||||||
Name string `xorm:"not null unique VARCHAR(20)"`
|
Name string `xorm:"not null unique VARCHAR(20)"`
|
||||||
Value string `xorm:"not null VARCHAR(20)"`
|
Value string `xorm:"not null VARCHAR(20)"`
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,8 @@ import "time"
|
|||||||
|
|
||||||
// Domain defines domain table
|
// Domain defines domain table
|
||||||
type Domain struct {
|
type Domain struct {
|
||||||
Domain string `xorm:"not null pk index(domain_domain_active) unique VARCHAR(255)"`
|
ID int `xorm:"not null pk unique serial"`
|
||||||
|
Domain string `xorm:"not null index(domain_domain_active) unique VARCHAR(255)"`
|
||||||
Description string `xorm:"not null default '' VARCHAR(255)"`
|
Description string `xorm:"not null default '' VARCHAR(255)"`
|
||||||
Aliases int `xorm:"not null default 0 INTEGER"`
|
Aliases int `xorm:"not null default 0 INTEGER"`
|
||||||
Mailboxes int `xorm:"not null default 0 INTEGER"`
|
Mailboxes int `xorm:"not null default 0 INTEGER"`
|
||||||
@ -12,8 +13,8 @@ type Domain struct {
|
|||||||
Quota int64 `xorm:"not null default 0 BIGINT"`
|
Quota int64 `xorm:"not null default 0 BIGINT"`
|
||||||
Transport string `xorm:"default 'NULL' VARCHAR(255)"`
|
Transport string `xorm:"default 'NULL' VARCHAR(255)"`
|
||||||
Backupmx bool `xorm:"not null default false BOOL"`
|
Backupmx bool `xorm:"not null default false BOOL"`
|
||||||
Created time.Time `xorm:"default now() TIMESTAMPZ"`
|
Created time.Time `xorm:"default now() TIMESTAMPZ created"`
|
||||||
Modified time.Time `xorm:"default now() TIMESTAMPZ"`
|
Modified time.Time `xorm:"default now() TIMESTAMPZ modified"`
|
||||||
Active bool `xorm:"not null default true index(domain_domain_active) BOOL"`
|
Active bool `xorm:"not null default true index(domain_domain_active) BOOL"`
|
||||||
PasswordExpiry int `xorm:"default 0 INTEGER"`
|
PasswordExpiry int `xorm:"default 0 INTEGER"`
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,9 @@ import "time"
|
|||||||
|
|
||||||
// DomainAdmins defines admin users of domains
|
// DomainAdmins defines admin users of domains
|
||||||
type DomainAdmins struct {
|
type DomainAdmins struct {
|
||||||
|
ID int `xorm:"not null pk unique serial"`
|
||||||
Username string `xorm:"not null VARCHAR(255)"`
|
Username string `xorm:"not null VARCHAR(255)"`
|
||||||
Domain string `xorm:"not null VARCHAR(255)"`
|
Domain string `xorm:"not null VARCHAR(255)"`
|
||||||
Created time.Time `xorm:"default now() TIMESTAMPZ"`
|
Created time.Time `xorm:"default now() TIMESTAMPZ created"`
|
||||||
Active bool `xorm:"not null default true BOOL"`
|
Active bool `xorm:"not null default true BOOL"`
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,6 @@ type Fetchmail struct {
|
|||||||
Sslfingerprint string `xorm:"default '' VARCHAR(255)"`
|
Sslfingerprint string `xorm:"default '' VARCHAR(255)"`
|
||||||
Domain string `xorm:"default '' VARCHAR(255)"`
|
Domain string `xorm:"default '' VARCHAR(255)"`
|
||||||
Active bool `xorm:"not null default false BOOL"`
|
Active bool `xorm:"not null default false BOOL"`
|
||||||
Created time.Time `xorm:"default '2000-01-01 00:00:00+01' TIMESTAMPZ"`
|
Created time.Time `xorm:"default '2000-01-01 00:00:00+01' TIMESTAMPZ created"`
|
||||||
Modified time.Time `xorm:"default now() TIMESTAMPZ"`
|
Modified time.Time `xorm:"default now() TIMESTAMPZ modified"`
|
||||||
}
|
}
|
||||||
|
@ -24,18 +24,18 @@ func (log *Log) APIFormat() *api.Log {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return &api.Log{
|
return &api.Log{
|
||||||
|
ID: log.ID,
|
||||||
Timestamp: log.Timestamp.Format(timetostring),
|
Timestamp: log.Timestamp.Format(timetostring),
|
||||||
Username: log.Username,
|
Username: log.Username,
|
||||||
Domain: log.Domain,
|
Domain: log.Domain,
|
||||||
Action: log.Action,
|
Action: log.Action,
|
||||||
Data: log.Data,
|
Data: log.Data,
|
||||||
ID: log.ID,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log ...
|
// Log ...
|
||||||
type Log struct {
|
type Log struct {
|
||||||
ID int `xorm:"not null pk autoincr INTEGER"`
|
ID int `xorm:"not null pk autoincr SERIAL"`
|
||||||
Timestamp time.Time `xorm:"default now() index(log_domain_timestamp_idx) TIMESTAMPZ"`
|
Timestamp time.Time `xorm:"default now() index(log_domain_timestamp_idx) TIMESTAMPZ"`
|
||||||
Username string `xorm:"not null default '' VARCHAR(255)"`
|
Username string `xorm:"not null default '' VARCHAR(255)"`
|
||||||
Domain string `xorm:"not null default '' index(log_domain_timestamp_idx) VARCHAR(255)"`
|
Domain string `xorm:"not null default '' index(log_domain_timestamp_idx) VARCHAR(255)"`
|
||||||
|
@ -2,10 +2,12 @@ package models
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.paulbsd.com/paulbsd/vmail/src/api"
|
"git.paulbsd.com/paulbsd/vmail/src/api"
|
||||||
"git.paulbsd.com/paulbsd/vmail/src/config"
|
"git.paulbsd.com/paulbsd/vmail/src/config"
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetMailboxes ...
|
// GetMailboxes ...
|
||||||
@ -21,7 +23,7 @@ func GetMailboxes(ctx *context.Context, config *config.Config) (apimailboxes []*
|
|||||||
// GetMailbox ...
|
// GetMailbox ...
|
||||||
func GetMailbox(ctx *context.Context, config *config.Config, id interface{}) (apimailbox *api.Mailbox, err error) {
|
func GetMailbox(ctx *context.Context, config *config.Config, id interface{}) (apimailbox *api.Mailbox, err error) {
|
||||||
var mailbox Mailbox
|
var mailbox Mailbox
|
||||||
has, err := config.Db.Where("username = ?", id).Get(&mailbox)
|
has, err := config.Db.Where(fmt.Sprintf("%s = ?", keyname), id).Get(&mailbox)
|
||||||
if !has || err != nil {
|
if !has || err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -29,12 +31,54 @@ func GetMailbox(ctx *context.Context, config *config.Config, id interface{}) (ap
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// APIFormat returns a JSON formatted object of Admin
|
// CreateMailbox ...
|
||||||
|
func CreateMailbox(ctx *context.Context, config *config.Config) (num int64, err error) {
|
||||||
|
var mailbox Mailbox
|
||||||
|
num, err = config.Db.Insert(&mailbox)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateMailbox ...
|
||||||
|
func UpdateMailbox(ctx *context.Context, config *config.Config, c echo.Context) (num int64, err error) {
|
||||||
|
var mailbox Mailbox
|
||||||
|
var apimailbox = new(api.Mailbox)
|
||||||
|
if err = c.Bind(apimailbox); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mailbox.APIParse(*apimailbox)
|
||||||
|
num, err = config.Db.ID(mailbox.ID).AllCols().Update(&mailbox)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// APIParse returns a JSON formatted object of Mailbox
|
||||||
|
func (alias *Mailbox) APIParse(apimailbox api.Mailbox) (err error) {
|
||||||
|
*alias = Mailbox{
|
||||||
|
ID: apimailbox.ID,
|
||||||
|
Username: apimailbox.Username,
|
||||||
|
Password: apimailbox.Password,
|
||||||
|
Name: apimailbox.Name,
|
||||||
|
Maildir: apimailbox.Maildir,
|
||||||
|
//Quota: apimailbox.Quota,
|
||||||
|
Active: apimailbox.Active,
|
||||||
|
Domain: apimailbox.Domain,
|
||||||
|
LocalPart: apimailbox.LocalPart,
|
||||||
|
Phone: apimailbox.Phone,
|
||||||
|
EmailOther: apimailbox.EmailOther,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// APIFormat returns a JSON formatted object of Mailbox
|
||||||
func (mailbox *Mailbox) APIFormat() *api.Mailbox {
|
func (mailbox *Mailbox) APIFormat() *api.Mailbox {
|
||||||
if mailbox == nil {
|
if mailbox == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return &api.Mailbox{
|
return &api.Mailbox{
|
||||||
|
ID: mailbox.ID,
|
||||||
Username: mailbox.Username,
|
Username: mailbox.Username,
|
||||||
Password: "****",
|
Password: "****",
|
||||||
Name: mailbox.Name,
|
Name: mailbox.Name,
|
||||||
@ -52,14 +96,15 @@ func (mailbox *Mailbox) APIFormat() *api.Mailbox {
|
|||||||
|
|
||||||
// Mailbox ...
|
// Mailbox ...
|
||||||
type Mailbox struct {
|
type Mailbox struct {
|
||||||
Username string `xorm:"not null pk unique index(mailbox_username_active) VARCHAR(255)"`
|
ID int `xorm:"not null pk serial"`
|
||||||
|
Username string `xorm:"not null index VARCHAR(255)"`
|
||||||
Password string `xorm:"not null default '' VARCHAR(255)"`
|
Password string `xorm:"not null default '' VARCHAR(255)"`
|
||||||
Name string `xorm:"not null default '' VARCHAR(255)"`
|
Name string `xorm:"not null default '' VARCHAR(255)"`
|
||||||
Maildir string `xorm:"not null default '' VARCHAR(255)"`
|
Maildir string `xorm:"not null default '' VARCHAR(255)"`
|
||||||
Quota int64 `xorm:"not null default 0 BIGINT"`
|
Quota int64 `xorm:"not null default 0 BIGINT"`
|
||||||
Created time.Time `xorm:"default now() TIMESTAMPZ"`
|
Created time.Time `xorm:"default now() TIMESTAMPZ created"`
|
||||||
Modified time.Time `xorm:"default now() TIMESTAMPZ"`
|
Modified time.Time `xorm:"default now() TIMESTAMPZ modified"`
|
||||||
Active bool `xorm:"default false index(mailbox_username_active) BOOL"`
|
Active bool `xorm:"default false index BOOL"`
|
||||||
Domain string `xorm:"index VARCHAR(255)"`
|
Domain string `xorm:"index VARCHAR(255)"`
|
||||||
LocalPart string `xorm:"not null VARCHAR(255)"`
|
LocalPart string `xorm:"not null VARCHAR(255)"`
|
||||||
Phone string `xorm:"not null default '' VARCHAR(30)"`
|
Phone string `xorm:"not null default '' VARCHAR(30)"`
|
||||||
|
@ -2,7 +2,8 @@ package models
|
|||||||
|
|
||||||
// Quota defines the Quota table
|
// Quota defines the Quota table
|
||||||
type Quota struct {
|
type Quota struct {
|
||||||
Username string `xorm:"not null pk VARCHAR(255)"`
|
ID int `xorm:"not null pk unique serial"`
|
||||||
Path string `xorm:"not null pk VARCHAR(100)"`
|
Username string `xorm:"not null unique VARCHAR(255)"`
|
||||||
|
Path string `xorm:"not null VARCHAR(100)"`
|
||||||
Current int64 `xorm:"BIGINT"`
|
Current int64 `xorm:"BIGINT"`
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,8 @@ package models
|
|||||||
|
|
||||||
// Quota2 defines the Quota2 table
|
// Quota2 defines the Quota2 table
|
||||||
type Quota2 struct {
|
type Quota2 struct {
|
||||||
Username string `xorm:"not null pk VARCHAR(100)"`
|
ID int `xorm:"not null pk unique serial"`
|
||||||
|
Username string `xorm:"not null unique VARCHAR(100)"`
|
||||||
Bytes int64 `xorm:"not null default 0 BIGINT"`
|
Bytes int64 `xorm:"not null default 0 BIGINT"`
|
||||||
Messages int `xorm:"not null default 0 INTEGER"`
|
Messages int `xorm:"not null default 0 INTEGER"`
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,9 @@ import (
|
|||||||
"git.paulbsd.com/paulbsd/vmail/utils/units"
|
"git.paulbsd.com/paulbsd/vmail/utils/units"
|
||||||
)
|
)
|
||||||
|
|
||||||
const timetostring = "02/01/2006 15:04"
|
// Mon Jan 2 15:04:05 MST 2006
|
||||||
|
const timetostring string = "02/01/2006 15:04:05"
|
||||||
|
const keyname string = "id"
|
||||||
|
|
||||||
func quotaFormat(in int64) (out string) {
|
func quotaFormat(in int64) (out string) {
|
||||||
var converted units.Base2Bytes
|
var converted units.Base2Bytes
|
||||||
|
@ -4,13 +4,14 @@ import "time"
|
|||||||
|
|
||||||
// Vacation defines the vacation table
|
// Vacation defines the vacation table
|
||||||
type Vacation struct {
|
type Vacation struct {
|
||||||
Email string `xorm:"not null pk index(vacation_email_active) VARCHAR(255)"`
|
ID int `xorm:"not null pk unique serial"`
|
||||||
|
Email string `xorm:"not null index(vacation_email_active) VARCHAR(255)"`
|
||||||
Subject string `xorm:"not null VARCHAR(255)"`
|
Subject string `xorm:"not null VARCHAR(255)"`
|
||||||
Body string `xorm:"not null default '' TEXT"`
|
Body string `xorm:"not null default '' TEXT"`
|
||||||
Created time.Time `xorm:"default now() TIMESTAMPZ"`
|
|
||||||
Active bool `xorm:"not null default true index(vacation_email_active) BOOL"`
|
Active bool `xorm:"not null default true index(vacation_email_active) BOOL"`
|
||||||
Domain string `xorm:"VARCHAR(255)"`
|
Domain string `xorm:"VARCHAR(255)"`
|
||||||
Modified time.Time `xorm:"default now() TIMESTAMPZ"`
|
Created time.Time `xorm:"default now() TIMESTAMPZ created"`
|
||||||
|
Modified time.Time `xorm:"default now() TIMESTAMPZ modified"`
|
||||||
Activefrom time.Time `xorm:"default '2000-01-01 00:00:00+01' TIMESTAMPZ"`
|
Activefrom time.Time `xorm:"default '2000-01-01 00:00:00+01' TIMESTAMPZ"`
|
||||||
Activeuntil time.Time `xorm:"default '2038-01-18 00:00:00+01' TIMESTAMPZ"`
|
Activeuntil time.Time `xorm:"default '2038-01-18 00:00:00+01' TIMESTAMPZ"`
|
||||||
IntervalTime int `xorm:"not null default 0 INTEGER"`
|
IntervalTime int `xorm:"not null default 0 INTEGER"`
|
||||||
|
@ -4,6 +4,7 @@ import "time"
|
|||||||
|
|
||||||
//VacationNotification defines the VacationNotification table
|
//VacationNotification defines the VacationNotification table
|
||||||
type VacationNotification struct {
|
type VacationNotification struct {
|
||||||
|
ID int `xorm:"not null pk unique serial"`
|
||||||
OnVacation string `xorm:"not null VARCHAR(255)"`
|
OnVacation string `xorm:"not null VARCHAR(255)"`
|
||||||
Notified string `xorm:"not null pk VARCHAR(255)"`
|
Notified string `xorm:"not null pk VARCHAR(255)"`
|
||||||
NotifiedAt time.Time `xorm:"not null default now() TIMESTAMPZ"`
|
NotifiedAt time.Time `xorm:"not null default now() TIMESTAMPZ"`
|
||||||
|
@ -2,6 +2,7 @@ package routers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"git.paulbsd.com/paulbsd/vmail/src/config"
|
"git.paulbsd.com/paulbsd/vmail/src/config"
|
||||||
@ -26,12 +27,24 @@ func RegisterRoutes(e *echo.Echo, ctx *context.Context, cfg *config.Config) {
|
|||||||
admins, err := models.GetAdmins(ctx, cfg)
|
admins, err := models.GetAdmins(ctx, cfg)
|
||||||
return c.JSON(http.StatusOK, admins)
|
return c.JSON(http.StatusOK, admins)
|
||||||
})
|
})
|
||||||
|
e.GET("/api/admin/model", func(c echo.Context) (err error) {
|
||||||
|
admins, err := models.GetAdminModel(ctx, cfg)
|
||||||
|
return c.JSON(http.StatusOK, admins)
|
||||||
|
})
|
||||||
e.GET("/api/admin/:id", func(c echo.Context) (err error) {
|
e.GET("/api/admin/:id", func(c echo.Context) (err error) {
|
||||||
admins, err := models.GetAdmins(ctx, cfg)
|
admins, err := models.GetAdmin(ctx, cfg, c.Param("id"))
|
||||||
|
return c.JSON(http.StatusOK, admins)
|
||||||
|
})
|
||||||
|
e.POST("/api/admin/:id", func(c echo.Context) (err error) {
|
||||||
|
admins, err := models.CreateAdmin(ctx, cfg, c.Param("id"))
|
||||||
|
return c.JSON(http.StatusOK, admins)
|
||||||
|
})
|
||||||
|
e.PUT("/api/admin/:id", func(c echo.Context) (err error) {
|
||||||
|
admins, err := models.UpdateAdmin(ctx, cfg, c.Param("id"))
|
||||||
return c.JSON(http.StatusOK, admins)
|
return c.JSON(http.StatusOK, admins)
|
||||||
})
|
})
|
||||||
e.DELETE("/api/admin/:id", func(c echo.Context) (err error) {
|
e.DELETE("/api/admin/:id", func(c echo.Context) (err error) {
|
||||||
admins, err := models.GetAdmins(ctx, cfg)
|
admins, err := models.DeleteAdmin(ctx, cfg, c.Param("id"))
|
||||||
return c.JSON(http.StatusOK, admins)
|
return c.JSON(http.StatusOK, admins)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -40,6 +53,10 @@ func RegisterRoutes(e *echo.Echo, ctx *context.Context, cfg *config.Config) {
|
|||||||
aliases, err := models.GetAliases(ctx, cfg)
|
aliases, err := models.GetAliases(ctx, cfg)
|
||||||
return c.JSON(http.StatusOK, aliases)
|
return c.JSON(http.StatusOK, aliases)
|
||||||
})
|
})
|
||||||
|
e.GET("/api/alias/model", func(c echo.Context) (err error) {
|
||||||
|
aliases, err := models.GetAliasModel(ctx, cfg)
|
||||||
|
return c.JSON(http.StatusOK, aliases)
|
||||||
|
})
|
||||||
e.GET("/api/alias/:id", func(c echo.Context) (err error) {
|
e.GET("/api/alias/:id", func(c echo.Context) (err error) {
|
||||||
alias, err := models.GetAlias(ctx, cfg, c.Param("id"))
|
alias, err := models.GetAlias(ctx, cfg, c.Param("id"))
|
||||||
return JSONResult(c, err, alias)
|
return JSONResult(c, err, alias)
|
||||||
@ -49,8 +66,11 @@ func RegisterRoutes(e *echo.Echo, ctx *context.Context, cfg *config.Config) {
|
|||||||
return c.JSON(http.StatusOK, nil)
|
return c.JSON(http.StatusOK, nil)
|
||||||
})
|
})
|
||||||
e.PUT("/api/alias/:id", func(c echo.Context) (err error) {
|
e.PUT("/api/alias/:id", func(c echo.Context) (err error) {
|
||||||
aliases, err := models.UpdateAlias(ctx, cfg, c.Param("id"))
|
_, err = models.UpdateAlias(ctx, cfg, c)
|
||||||
return c.JSON(http.StatusOK, aliases)
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, fmt.Sprintf("Error %s", err))
|
||||||
|
}
|
||||||
|
return c.JSON(http.StatusOK, nil)
|
||||||
})
|
})
|
||||||
e.DELETE("/api/alias/:id", func(c echo.Context) (err error) {
|
e.DELETE("/api/alias/:id", func(c echo.Context) (err error) {
|
||||||
aliases, err := models.DeleteAlias(ctx, cfg, c.Param("id"))
|
aliases, err := models.DeleteAlias(ctx, cfg, c.Param("id"))
|
||||||
@ -66,6 +86,14 @@ func RegisterRoutes(e *echo.Echo, ctx *context.Context, cfg *config.Config) {
|
|||||||
mailbox, err := models.GetMailbox(ctx, cfg, c.Param("id"))
|
mailbox, err := models.GetMailbox(ctx, cfg, c.Param("id"))
|
||||||
return c.JSON(http.StatusOK, mailbox)
|
return c.JSON(http.StatusOK, mailbox)
|
||||||
})
|
})
|
||||||
|
e.POST("/api/mailbox", func(c echo.Context) (err error) {
|
||||||
|
mailbox, err := models.CreateMailbox(ctx, cfg)
|
||||||
|
return c.JSON(http.StatusOK, mailbox)
|
||||||
|
})
|
||||||
|
e.PUT("/api/mailbox/:id", func(c echo.Context) (err error) {
|
||||||
|
mailbox, err := models.UpdateMailbox(ctx, cfg, c)
|
||||||
|
return c.JSON(http.StatusOK, mailbox)
|
||||||
|
})
|
||||||
e.DELETE("/api/mailbox/:id", func(c echo.Context) (err error) {
|
e.DELETE("/api/mailbox/:id", func(c echo.Context) (err error) {
|
||||||
mailbox, err := models.GetMailbox(ctx, cfg, c.Param("id"))
|
mailbox, err := models.GetMailbox(ctx, cfg, c.Param("id"))
|
||||||
return c.JSON(http.StatusOK, mailbox)
|
return c.JSON(http.StatusOK, mailbox)
|
||||||
@ -78,7 +106,7 @@ func RegisterRoutes(e *echo.Echo, ctx *context.Context, cfg *config.Config) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSONResult ...
|
// JSONResult handles returns and error management on backend api
|
||||||
func JSONResult(c echo.Context, inputerr error, data interface{}) (err error) {
|
func JSONResult(c echo.Context, inputerr error, data interface{}) (err error) {
|
||||||
if inputerr != nil {
|
if inputerr != nil {
|
||||||
return c.JSON(http.StatusInternalServerError, nil)
|
return c.JSON(http.StatusInternalServerError, nil)
|
||||||
|
@ -17,7 +17,7 @@ func RunServer(ctx *context.Context, cfg *config.Config) (err error) {
|
|||||||
e.HideBanner = true
|
e.HideBanner = true
|
||||||
|
|
||||||
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
|
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
|
||||||
AllowOrigins: []string{"http://localhost:8081"},
|
AllowOrigins: []string{"http://localhost:8080"},
|
||||||
AllowMethods: []string{http.MethodGet, http.MethodPut, http.MethodPost, http.MethodDelete},
|
AllowMethods: []string{http.MethodGet, http.MethodPut, http.MethodPost, http.MethodDelete},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
2
vendor/github.com/golang/snappy/AUTHORS
generated
vendored
2
vendor/github.com/golang/snappy/AUTHORS
generated
vendored
@ -8,8 +8,10 @@
|
|||||||
|
|
||||||
# Please keep the list sorted.
|
# Please keep the list sorted.
|
||||||
|
|
||||||
|
Amazon.com, Inc
|
||||||
Damian Gryski <dgryski@gmail.com>
|
Damian Gryski <dgryski@gmail.com>
|
||||||
Google Inc.
|
Google Inc.
|
||||||
Jan Mercl <0xjnml@gmail.com>
|
Jan Mercl <0xjnml@gmail.com>
|
||||||
|
Klaus Post <klauspost@gmail.com>
|
||||||
Rodolfo Carvalho <rhcarvalho@gmail.com>
|
Rodolfo Carvalho <rhcarvalho@gmail.com>
|
||||||
Sebastien Binet <seb.binet@gmail.com>
|
Sebastien Binet <seb.binet@gmail.com>
|
||||||
|
2
vendor/github.com/golang/snappy/CONTRIBUTORS
generated
vendored
2
vendor/github.com/golang/snappy/CONTRIBUTORS
generated
vendored
@ -28,7 +28,9 @@
|
|||||||
|
|
||||||
Damian Gryski <dgryski@gmail.com>
|
Damian Gryski <dgryski@gmail.com>
|
||||||
Jan Mercl <0xjnml@gmail.com>
|
Jan Mercl <0xjnml@gmail.com>
|
||||||
|
Jonathan Swinney <jswinney@amazon.com>
|
||||||
Kai Backman <kaib@golang.org>
|
Kai Backman <kaib@golang.org>
|
||||||
|
Klaus Post <klauspost@gmail.com>
|
||||||
Marc-Antoine Ruel <maruel@chromium.org>
|
Marc-Antoine Ruel <maruel@chromium.org>
|
||||||
Nigel Tao <nigeltao@golang.org>
|
Nigel Tao <nigeltao@golang.org>
|
||||||
Rob Pike <r@golang.org>
|
Rob Pike <r@golang.org>
|
||||||
|
4
vendor/github.com/golang/snappy/decode.go
generated
vendored
4
vendor/github.com/golang/snappy/decode.go
generated
vendored
@ -52,6 +52,8 @@ const (
|
|||||||
// Otherwise, a newly allocated slice will be returned.
|
// Otherwise, a newly allocated slice will be returned.
|
||||||
//
|
//
|
||||||
// The dst and src must not overlap. It is valid to pass a nil dst.
|
// The dst and src must not overlap. It is valid to pass a nil dst.
|
||||||
|
//
|
||||||
|
// Decode handles the Snappy block format, not the Snappy stream format.
|
||||||
func Decode(dst, src []byte) ([]byte, error) {
|
func Decode(dst, src []byte) ([]byte, error) {
|
||||||
dLen, s, err := decodedLen(src)
|
dLen, s, err := decodedLen(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -83,6 +85,8 @@ func NewReader(r io.Reader) *Reader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reader is an io.Reader that can read Snappy-compressed bytes.
|
// Reader is an io.Reader that can read Snappy-compressed bytes.
|
||||||
|
//
|
||||||
|
// Reader handles the Snappy stream format, not the Snappy block format.
|
||||||
type Reader struct {
|
type Reader struct {
|
||||||
r io.Reader
|
r io.Reader
|
||||||
err error
|
err error
|
||||||
|
494
vendor/github.com/golang/snappy/decode_arm64.s
generated
vendored
Normal file
494
vendor/github.com/golang/snappy/decode_arm64.s
generated
vendored
Normal file
@ -0,0 +1,494 @@
|
|||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !appengine
|
||||||
|
// +build gc
|
||||||
|
// +build !noasm
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// The asm code generally follows the pure Go code in decode_other.go, except
|
||||||
|
// where marked with a "!!!".
|
||||||
|
|
||||||
|
// func decode(dst, src []byte) int
|
||||||
|
//
|
||||||
|
// All local variables fit into registers. The non-zero stack size is only to
|
||||||
|
// spill registers and push args when issuing a CALL. The register allocation:
|
||||||
|
// - R2 scratch
|
||||||
|
// - R3 scratch
|
||||||
|
// - R4 length or x
|
||||||
|
// - R5 offset
|
||||||
|
// - R6 &src[s]
|
||||||
|
// - R7 &dst[d]
|
||||||
|
// + R8 dst_base
|
||||||
|
// + R9 dst_len
|
||||||
|
// + R10 dst_base + dst_len
|
||||||
|
// + R11 src_base
|
||||||
|
// + R12 src_len
|
||||||
|
// + R13 src_base + src_len
|
||||||
|
// - R14 used by doCopy
|
||||||
|
// - R15 used by doCopy
|
||||||
|
//
|
||||||
|
// The registers R8-R13 (marked with a "+") are set at the start of the
|
||||||
|
// function, and after a CALL returns, and are not otherwise modified.
|
||||||
|
//
|
||||||
|
// The d variable is implicitly R7 - R8, and len(dst)-d is R10 - R7.
|
||||||
|
// The s variable is implicitly R6 - R11, and len(src)-s is R13 - R6.
|
||||||
|
TEXT ·decode(SB), NOSPLIT, $56-56
|
||||||
|
// Initialize R6, R7 and R8-R13.
|
||||||
|
MOVD dst_base+0(FP), R8
|
||||||
|
MOVD dst_len+8(FP), R9
|
||||||
|
MOVD R8, R7
|
||||||
|
MOVD R8, R10
|
||||||
|
ADD R9, R10, R10
|
||||||
|
MOVD src_base+24(FP), R11
|
||||||
|
MOVD src_len+32(FP), R12
|
||||||
|
MOVD R11, R6
|
||||||
|
MOVD R11, R13
|
||||||
|
ADD R12, R13, R13
|
||||||
|
|
||||||
|
loop:
|
||||||
|
// for s < len(src)
|
||||||
|
CMP R13, R6
|
||||||
|
BEQ end
|
||||||
|
|
||||||
|
// R4 = uint32(src[s])
|
||||||
|
//
|
||||||
|
// switch src[s] & 0x03
|
||||||
|
MOVBU (R6), R4
|
||||||
|
MOVW R4, R3
|
||||||
|
ANDW $3, R3
|
||||||
|
MOVW $1, R1
|
||||||
|
CMPW R1, R3
|
||||||
|
BGE tagCopy
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// The code below handles literal tags.
|
||||||
|
|
||||||
|
// case tagLiteral:
|
||||||
|
// x := uint32(src[s] >> 2)
|
||||||
|
// switch
|
||||||
|
MOVW $60, R1
|
||||||
|
LSRW $2, R4, R4
|
||||||
|
CMPW R4, R1
|
||||||
|
BLS tagLit60Plus
|
||||||
|
|
||||||
|
// case x < 60:
|
||||||
|
// s++
|
||||||
|
ADD $1, R6, R6
|
||||||
|
|
||||||
|
doLit:
|
||||||
|
// This is the end of the inner "switch", when we have a literal tag.
|
||||||
|
//
|
||||||
|
// We assume that R4 == x and x fits in a uint32, where x is the variable
|
||||||
|
// used in the pure Go decode_other.go code.
|
||||||
|
|
||||||
|
// length = int(x) + 1
|
||||||
|
//
|
||||||
|
// Unlike the pure Go code, we don't need to check if length <= 0 because
|
||||||
|
// R4 can hold 64 bits, so the increment cannot overflow.
|
||||||
|
ADD $1, R4, R4
|
||||||
|
|
||||||
|
// Prepare to check if copying length bytes will run past the end of dst or
|
||||||
|
// src.
|
||||||
|
//
|
||||||
|
// R2 = len(dst) - d
|
||||||
|
// R3 = len(src) - s
|
||||||
|
MOVD R10, R2
|
||||||
|
SUB R7, R2, R2
|
||||||
|
MOVD R13, R3
|
||||||
|
SUB R6, R3, R3
|
||||||
|
|
||||||
|
// !!! Try a faster technique for short (16 or fewer bytes) copies.
|
||||||
|
//
|
||||||
|
// if length > 16 || len(dst)-d < 16 || len(src)-s < 16 {
|
||||||
|
// goto callMemmove // Fall back on calling runtime·memmove.
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// The C++ snappy code calls this TryFastAppend. It also checks len(src)-s
|
||||||
|
// against 21 instead of 16, because it cannot assume that all of its input
|
||||||
|
// is contiguous in memory and so it needs to leave enough source bytes to
|
||||||
|
// read the next tag without refilling buffers, but Go's Decode assumes
|
||||||
|
// contiguousness (the src argument is a []byte).
|
||||||
|
CMP $16, R4
|
||||||
|
BGT callMemmove
|
||||||
|
CMP $16, R2
|
||||||
|
BLT callMemmove
|
||||||
|
CMP $16, R3
|
||||||
|
BLT callMemmove
|
||||||
|
|
||||||
|
// !!! Implement the copy from src to dst as a 16-byte load and store.
|
||||||
|
// (Decode's documentation says that dst and src must not overlap.)
|
||||||
|
//
|
||||||
|
// This always copies 16 bytes, instead of only length bytes, but that's
|
||||||
|
// OK. If the input is a valid Snappy encoding then subsequent iterations
|
||||||
|
// will fix up the overrun. Otherwise, Decode returns a nil []byte (and a
|
||||||
|
// non-nil error), so the overrun will be ignored.
|
||||||
|
//
|
||||||
|
// Note that on arm64, it is legal and cheap to issue unaligned 8-byte or
|
||||||
|
// 16-byte loads and stores. This technique probably wouldn't be as
|
||||||
|
// effective on architectures that are fussier about alignment.
|
||||||
|
LDP 0(R6), (R14, R15)
|
||||||
|
STP (R14, R15), 0(R7)
|
||||||
|
|
||||||
|
// d += length
|
||||||
|
// s += length
|
||||||
|
ADD R4, R7, R7
|
||||||
|
ADD R4, R6, R6
|
||||||
|
B loop
|
||||||
|
|
||||||
|
callMemmove:
|
||||||
|
// if length > len(dst)-d || length > len(src)-s { etc }
|
||||||
|
CMP R2, R4
|
||||||
|
BGT errCorrupt
|
||||||
|
CMP R3, R4
|
||||||
|
BGT errCorrupt
|
||||||
|
|
||||||
|
// copy(dst[d:], src[s:s+length])
|
||||||
|
//
|
||||||
|
// This means calling runtime·memmove(&dst[d], &src[s], length), so we push
|
||||||
|
// R7, R6 and R4 as arguments. Coincidentally, we also need to spill those
|
||||||
|
// three registers to the stack, to save local variables across the CALL.
|
||||||
|
MOVD R7, 8(RSP)
|
||||||
|
MOVD R6, 16(RSP)
|
||||||
|
MOVD R4, 24(RSP)
|
||||||
|
MOVD R7, 32(RSP)
|
||||||
|
MOVD R6, 40(RSP)
|
||||||
|
MOVD R4, 48(RSP)
|
||||||
|
CALL runtime·memmove(SB)
|
||||||
|
|
||||||
|
// Restore local variables: unspill registers from the stack and
|
||||||
|
// re-calculate R8-R13.
|
||||||
|
MOVD 32(RSP), R7
|
||||||
|
MOVD 40(RSP), R6
|
||||||
|
MOVD 48(RSP), R4
|
||||||
|
MOVD dst_base+0(FP), R8
|
||||||
|
MOVD dst_len+8(FP), R9
|
||||||
|
MOVD R8, R10
|
||||||
|
ADD R9, R10, R10
|
||||||
|
MOVD src_base+24(FP), R11
|
||||||
|
MOVD src_len+32(FP), R12
|
||||||
|
MOVD R11, R13
|
||||||
|
ADD R12, R13, R13
|
||||||
|
|
||||||
|
// d += length
|
||||||
|
// s += length
|
||||||
|
ADD R4, R7, R7
|
||||||
|
ADD R4, R6, R6
|
||||||
|
B loop
|
||||||
|
|
||||||
|
tagLit60Plus:
|
||||||
|
// !!! This fragment does the
|
||||||
|
//
|
||||||
|
// s += x - 58; if uint(s) > uint(len(src)) { etc }
|
||||||
|
//
|
||||||
|
// checks. In the asm version, we code it once instead of once per switch case.
|
||||||
|
ADD R4, R6, R6
|
||||||
|
SUB $58, R6, R6
|
||||||
|
MOVD R6, R3
|
||||||
|
SUB R11, R3, R3
|
||||||
|
CMP R12, R3
|
||||||
|
BGT errCorrupt
|
||||||
|
|
||||||
|
// case x == 60:
|
||||||
|
MOVW $61, R1
|
||||||
|
CMPW R1, R4
|
||||||
|
BEQ tagLit61
|
||||||
|
BGT tagLit62Plus
|
||||||
|
|
||||||
|
// x = uint32(src[s-1])
|
||||||
|
MOVBU -1(R6), R4
|
||||||
|
B doLit
|
||||||
|
|
||||||
|
tagLit61:
|
||||||
|
// case x == 61:
|
||||||
|
// x = uint32(src[s-2]) | uint32(src[s-1])<<8
|
||||||
|
MOVHU -2(R6), R4
|
||||||
|
B doLit
|
||||||
|
|
||||||
|
tagLit62Plus:
|
||||||
|
CMPW $62, R4
|
||||||
|
BHI tagLit63
|
||||||
|
|
||||||
|
// case x == 62:
|
||||||
|
// x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16
|
||||||
|
MOVHU -3(R6), R4
|
||||||
|
MOVBU -1(R6), R3
|
||||||
|
ORR R3<<16, R4
|
||||||
|
B doLit
|
||||||
|
|
||||||
|
tagLit63:
|
||||||
|
// case x == 63:
|
||||||
|
// x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24
|
||||||
|
MOVWU -4(R6), R4
|
||||||
|
B doLit
|
||||||
|
|
||||||
|
// The code above handles literal tags.
|
||||||
|
// ----------------------------------------
|
||||||
|
// The code below handles copy tags.
|
||||||
|
|
||||||
|
tagCopy4:
|
||||||
|
// case tagCopy4:
|
||||||
|
// s += 5
|
||||||
|
ADD $5, R6, R6
|
||||||
|
|
||||||
|
// if uint(s) > uint(len(src)) { etc }
|
||||||
|
MOVD R6, R3
|
||||||
|
SUB R11, R3, R3
|
||||||
|
CMP R12, R3
|
||||||
|
BGT errCorrupt
|
||||||
|
|
||||||
|
// length = 1 + int(src[s-5])>>2
|
||||||
|
MOVD $1, R1
|
||||||
|
ADD R4>>2, R1, R4
|
||||||
|
|
||||||
|
// offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24)
|
||||||
|
MOVWU -4(R6), R5
|
||||||
|
B doCopy
|
||||||
|
|
||||||
|
tagCopy2:
|
||||||
|
// case tagCopy2:
|
||||||
|
// s += 3
|
||||||
|
ADD $3, R6, R6
|
||||||
|
|
||||||
|
// if uint(s) > uint(len(src)) { etc }
|
||||||
|
MOVD R6, R3
|
||||||
|
SUB R11, R3, R3
|
||||||
|
CMP R12, R3
|
||||||
|
BGT errCorrupt
|
||||||
|
|
||||||
|
// length = 1 + int(src[s-3])>>2
|
||||||
|
MOVD $1, R1
|
||||||
|
ADD R4>>2, R1, R4
|
||||||
|
|
||||||
|
// offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8)
|
||||||
|
MOVHU -2(R6), R5
|
||||||
|
B doCopy
|
||||||
|
|
||||||
|
tagCopy:
|
||||||
|
// We have a copy tag. We assume that:
|
||||||
|
// - R3 == src[s] & 0x03
|
||||||
|
// - R4 == src[s]
|
||||||
|
CMP $2, R3
|
||||||
|
BEQ tagCopy2
|
||||||
|
BGT tagCopy4
|
||||||
|
|
||||||
|
// case tagCopy1:
|
||||||
|
// s += 2
|
||||||
|
ADD $2, R6, R6
|
||||||
|
|
||||||
|
// if uint(s) > uint(len(src)) { etc }
|
||||||
|
MOVD R6, R3
|
||||||
|
SUB R11, R3, R3
|
||||||
|
CMP R12, R3
|
||||||
|
BGT errCorrupt
|
||||||
|
|
||||||
|
// offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1]))
|
||||||
|
MOVD R4, R5
|
||||||
|
AND $0xe0, R5
|
||||||
|
MOVBU -1(R6), R3
|
||||||
|
ORR R5<<3, R3, R5
|
||||||
|
|
||||||
|
// length = 4 + int(src[s-2])>>2&0x7
|
||||||
|
MOVD $7, R1
|
||||||
|
AND R4>>2, R1, R4
|
||||||
|
ADD $4, R4, R4
|
||||||
|
|
||||||
|
doCopy:
|
||||||
|
// This is the end of the outer "switch", when we have a copy tag.
|
||||||
|
//
|
||||||
|
// We assume that:
|
||||||
|
// - R4 == length && R4 > 0
|
||||||
|
// - R5 == offset
|
||||||
|
|
||||||
|
// if offset <= 0 { etc }
|
||||||
|
MOVD $0, R1
|
||||||
|
CMP R1, R5
|
||||||
|
BLE errCorrupt
|
||||||
|
|
||||||
|
// if d < offset { etc }
|
||||||
|
MOVD R7, R3
|
||||||
|
SUB R8, R3, R3
|
||||||
|
CMP R5, R3
|
||||||
|
BLT errCorrupt
|
||||||
|
|
||||||
|
// if length > len(dst)-d { etc }
|
||||||
|
MOVD R10, R3
|
||||||
|
SUB R7, R3, R3
|
||||||
|
CMP R3, R4
|
||||||
|
BGT errCorrupt
|
||||||
|
|
||||||
|
// forwardCopy(dst[d:d+length], dst[d-offset:]); d += length
|
||||||
|
//
|
||||||
|
// Set:
|
||||||
|
// - R14 = len(dst)-d
|
||||||
|
// - R15 = &dst[d-offset]
|
||||||
|
MOVD R10, R14
|
||||||
|
SUB R7, R14, R14
|
||||||
|
MOVD R7, R15
|
||||||
|
SUB R5, R15, R15
|
||||||
|
|
||||||
|
// !!! Try a faster technique for short (16 or fewer bytes) forward copies.
|
||||||
|
//
|
||||||
|
// First, try using two 8-byte load/stores, similar to the doLit technique
|
||||||
|
// above. Even if dst[d:d+length] and dst[d-offset:] can overlap, this is
|
||||||
|
// still OK if offset >= 8. Note that this has to be two 8-byte load/stores
|
||||||
|
// and not one 16-byte load/store, and the first store has to be before the
|
||||||
|
// second load, due to the overlap if offset is in the range [8, 16).
|
||||||
|
//
|
||||||
|
// if length > 16 || offset < 8 || len(dst)-d < 16 {
|
||||||
|
// goto slowForwardCopy
|
||||||
|
// }
|
||||||
|
// copy 16 bytes
|
||||||
|
// d += length
|
||||||
|
CMP $16, R4
|
||||||
|
BGT slowForwardCopy
|
||||||
|
CMP $8, R5
|
||||||
|
BLT slowForwardCopy
|
||||||
|
CMP $16, R14
|
||||||
|
BLT slowForwardCopy
|
||||||
|
MOVD 0(R15), R2
|
||||||
|
MOVD R2, 0(R7)
|
||||||
|
MOVD 8(R15), R3
|
||||||
|
MOVD R3, 8(R7)
|
||||||
|
ADD R4, R7, R7
|
||||||
|
B loop
|
||||||
|
|
||||||
|
slowForwardCopy:
|
||||||
|
// !!! If the forward copy is longer than 16 bytes, or if offset < 8, we
|
||||||
|
// can still try 8-byte load stores, provided we can overrun up to 10 extra
|
||||||
|
// bytes. As above, the overrun will be fixed up by subsequent iterations
|
||||||
|
// of the outermost loop.
|
||||||
|
//
|
||||||
|
// The C++ snappy code calls this technique IncrementalCopyFastPath. Its
|
||||||
|
// commentary says:
|
||||||
|
//
|
||||||
|
// ----
|
||||||
|
//
|
||||||
|
// The main part of this loop is a simple copy of eight bytes at a time
|
||||||
|
// until we've copied (at least) the requested amount of bytes. However,
|
||||||
|
// if d and d-offset are less than eight bytes apart (indicating a
|
||||||
|
// repeating pattern of length < 8), we first need to expand the pattern in
|
||||||
|
// order to get the correct results. For instance, if the buffer looks like
|
||||||
|
// this, with the eight-byte <d-offset> and <d> patterns marked as
|
||||||
|
// intervals:
|
||||||
|
//
|
||||||
|
// abxxxxxxxxxxxx
|
||||||
|
// [------] d-offset
|
||||||
|
// [------] d
|
||||||
|
//
|
||||||
|
// a single eight-byte copy from <d-offset> to <d> will repeat the pattern
|
||||||
|
// once, after which we can move <d> two bytes without moving <d-offset>:
|
||||||
|
//
|
||||||
|
// ababxxxxxxxxxx
|
||||||
|
// [------] d-offset
|
||||||
|
// [------] d
|
||||||
|
//
|
||||||
|
// and repeat the exercise until the two no longer overlap.
|
||||||
|
//
|
||||||
|
// This allows us to do very well in the special case of one single byte
|
||||||
|
// repeated many times, without taking a big hit for more general cases.
|
||||||
|
//
|
||||||
|
// The worst case of extra writing past the end of the match occurs when
|
||||||
|
// offset == 1 and length == 1; the last copy will read from byte positions
|
||||||
|
// [0..7] and write to [4..11], whereas it was only supposed to write to
|
||||||
|
// position 1. Thus, ten excess bytes.
|
||||||
|
//
|
||||||
|
// ----
|
||||||
|
//
|
||||||
|
// That "10 byte overrun" worst case is confirmed by Go's
|
||||||
|
// TestSlowForwardCopyOverrun, which also tests the fixUpSlowForwardCopy
|
||||||
|
// and finishSlowForwardCopy algorithm.
|
||||||
|
//
|
||||||
|
// if length > len(dst)-d-10 {
|
||||||
|
// goto verySlowForwardCopy
|
||||||
|
// }
|
||||||
|
SUB $10, R14, R14
|
||||||
|
CMP R14, R4
|
||||||
|
BGT verySlowForwardCopy
|
||||||
|
|
||||||
|
makeOffsetAtLeast8:
|
||||||
|
// !!! As above, expand the pattern so that offset >= 8 and we can use
|
||||||
|
// 8-byte load/stores.
|
||||||
|
//
|
||||||
|
// for offset < 8 {
|
||||||
|
// copy 8 bytes from dst[d-offset:] to dst[d:]
|
||||||
|
// length -= offset
|
||||||
|
// d += offset
|
||||||
|
// offset += offset
|
||||||
|
// // The two previous lines together means that d-offset, and therefore
|
||||||
|
// // R15, is unchanged.
|
||||||
|
// }
|
||||||
|
CMP $8, R5
|
||||||
|
BGE fixUpSlowForwardCopy
|
||||||
|
MOVD (R15), R3
|
||||||
|
MOVD R3, (R7)
|
||||||
|
SUB R5, R4, R4
|
||||||
|
ADD R5, R7, R7
|
||||||
|
ADD R5, R5, R5
|
||||||
|
B makeOffsetAtLeast8
|
||||||
|
|
||||||
|
fixUpSlowForwardCopy:
|
||||||
|
// !!! Add length (which might be negative now) to d (implied by R7 being
|
||||||
|
// &dst[d]) so that d ends up at the right place when we jump back to the
|
||||||
|
// top of the loop. Before we do that, though, we save R7 to R2 so that, if
|
||||||
|
// length is positive, copying the remaining length bytes will write to the
|
||||||
|
// right place.
|
||||||
|
MOVD R7, R2
|
||||||
|
ADD R4, R7, R7
|
||||||
|
|
||||||
|
finishSlowForwardCopy:
|
||||||
|
// !!! Repeat 8-byte load/stores until length <= 0. Ending with a negative
|
||||||
|
// length means that we overrun, but as above, that will be fixed up by
|
||||||
|
// subsequent iterations of the outermost loop.
|
||||||
|
MOVD $0, R1
|
||||||
|
CMP R1, R4
|
||||||
|
BLE loop
|
||||||
|
MOVD (R15), R3
|
||||||
|
MOVD R3, (R2)
|
||||||
|
ADD $8, R15, R15
|
||||||
|
ADD $8, R2, R2
|
||||||
|
SUB $8, R4, R4
|
||||||
|
B finishSlowForwardCopy
|
||||||
|
|
||||||
|
verySlowForwardCopy:
|
||||||
|
// verySlowForwardCopy is a simple implementation of forward copy. In C
|
||||||
|
// parlance, this is a do/while loop instead of a while loop, since we know
|
||||||
|
// that length > 0. In Go syntax:
|
||||||
|
//
|
||||||
|
// for {
|
||||||
|
// dst[d] = dst[d - offset]
|
||||||
|
// d++
|
||||||
|
// length--
|
||||||
|
// if length == 0 {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
MOVB (R15), R3
|
||||||
|
MOVB R3, (R7)
|
||||||
|
ADD $1, R15, R15
|
||||||
|
ADD $1, R7, R7
|
||||||
|
SUB $1, R4, R4
|
||||||
|
CBNZ R4, verySlowForwardCopy
|
||||||
|
B loop
|
||||||
|
|
||||||
|
// The code above handles copy tags.
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
end:
|
||||||
|
// This is the end of the "for s < len(src)".
|
||||||
|
//
|
||||||
|
// if d != len(dst) { etc }
|
||||||
|
CMP R10, R7
|
||||||
|
BNE errCorrupt
|
||||||
|
|
||||||
|
// return 0
|
||||||
|
MOVD $0, ret+48(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
errCorrupt:
|
||||||
|
// return decodeErrCodeCorrupt
|
||||||
|
MOVD $1, R2
|
||||||
|
MOVD R2, ret+48(FP)
|
||||||
|
RET
|
@ -5,6 +5,7 @@
|
|||||||
// +build !appengine
|
// +build !appengine
|
||||||
// +build gc
|
// +build gc
|
||||||
// +build !noasm
|
// +build !noasm
|
||||||
|
// +build amd64 arm64
|
||||||
|
|
||||||
package snappy
|
package snappy
|
||||||
|
|
24
vendor/github.com/golang/snappy/decode_other.go
generated
vendored
24
vendor/github.com/golang/snappy/decode_other.go
generated
vendored
@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build !amd64 appengine !gc noasm
|
// +build !amd64,!arm64 appengine !gc noasm
|
||||||
|
|
||||||
package snappy
|
package snappy
|
||||||
|
|
||||||
@ -85,14 +85,28 @@ func decode(dst, src []byte) int {
|
|||||||
if offset <= 0 || d < offset || length > len(dst)-d {
|
if offset <= 0 || d < offset || length > len(dst)-d {
|
||||||
return decodeErrCodeCorrupt
|
return decodeErrCodeCorrupt
|
||||||
}
|
}
|
||||||
// Copy from an earlier sub-slice of dst to a later sub-slice. Unlike
|
// Copy from an earlier sub-slice of dst to a later sub-slice.
|
||||||
// the built-in copy function, this byte-by-byte copy always runs
|
// If no overlap, use the built-in copy:
|
||||||
|
if offset >= length {
|
||||||
|
copy(dst[d:d+length], dst[d-offset:])
|
||||||
|
d += length
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlike the built-in copy function, this byte-by-byte copy always runs
|
||||||
// forwards, even if the slices overlap. Conceptually, this is:
|
// forwards, even if the slices overlap. Conceptually, this is:
|
||||||
//
|
//
|
||||||
// d += forwardCopy(dst[d:d+length], dst[d-offset:])
|
// d += forwardCopy(dst[d:d+length], dst[d-offset:])
|
||||||
for end := d + length; d != end; d++ {
|
//
|
||||||
dst[d] = dst[d-offset]
|
// We align the slices into a and b and show the compiler they are the same size.
|
||||||
|
// This allows the loop to run without bounds checks.
|
||||||
|
a := dst[d : d+length]
|
||||||
|
b := dst[d-offset:]
|
||||||
|
b = b[:len(a)]
|
||||||
|
for i := range a {
|
||||||
|
a[i] = b[i]
|
||||||
}
|
}
|
||||||
|
d += length
|
||||||
}
|
}
|
||||||
if d != len(dst) {
|
if d != len(dst) {
|
||||||
return decodeErrCodeCorrupt
|
return decodeErrCodeCorrupt
|
||||||
|
4
vendor/github.com/golang/snappy/encode.go
generated
vendored
4
vendor/github.com/golang/snappy/encode.go
generated
vendored
@ -15,6 +15,8 @@ import (
|
|||||||
// Otherwise, a newly allocated slice will be returned.
|
// Otherwise, a newly allocated slice will be returned.
|
||||||
//
|
//
|
||||||
// The dst and src must not overlap. It is valid to pass a nil dst.
|
// The dst and src must not overlap. It is valid to pass a nil dst.
|
||||||
|
//
|
||||||
|
// Encode handles the Snappy block format, not the Snappy stream format.
|
||||||
func Encode(dst, src []byte) []byte {
|
func Encode(dst, src []byte) []byte {
|
||||||
if n := MaxEncodedLen(len(src)); n < 0 {
|
if n := MaxEncodedLen(len(src)); n < 0 {
|
||||||
panic(ErrTooLarge)
|
panic(ErrTooLarge)
|
||||||
@ -139,6 +141,8 @@ func NewBufferedWriter(w io.Writer) *Writer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Writer is an io.Writer that can write Snappy-compressed bytes.
|
// Writer is an io.Writer that can write Snappy-compressed bytes.
|
||||||
|
//
|
||||||
|
// Writer handles the Snappy stream format, not the Snappy block format.
|
||||||
type Writer struct {
|
type Writer struct {
|
||||||
w io.Writer
|
w io.Writer
|
||||||
err error
|
err error
|
||||||
|
722
vendor/github.com/golang/snappy/encode_arm64.s
generated
vendored
Normal file
722
vendor/github.com/golang/snappy/encode_arm64.s
generated
vendored
Normal file
@ -0,0 +1,722 @@
|
|||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !appengine
|
||||||
|
// +build gc
|
||||||
|
// +build !noasm
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// The asm code generally follows the pure Go code in encode_other.go, except
|
||||||
|
// where marked with a "!!!".
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// func emitLiteral(dst, lit []byte) int
|
||||||
|
//
|
||||||
|
// All local variables fit into registers. The register allocation:
|
||||||
|
// - R3 len(lit)
|
||||||
|
// - R4 n
|
||||||
|
// - R6 return value
|
||||||
|
// - R8 &dst[i]
|
||||||
|
// - R10 &lit[0]
|
||||||
|
//
|
||||||
|
// The 32 bytes of stack space is to call runtime·memmove.
|
||||||
|
//
|
||||||
|
// The unusual register allocation of local variables, such as R10 for the
|
||||||
|
// source pointer, matches the allocation used at the call site in encodeBlock,
|
||||||
|
// which makes it easier to manually inline this function.
|
||||||
|
TEXT ·emitLiteral(SB), NOSPLIT, $32-56
|
||||||
|
MOVD dst_base+0(FP), R8
|
||||||
|
MOVD lit_base+24(FP), R10
|
||||||
|
MOVD lit_len+32(FP), R3
|
||||||
|
MOVD R3, R6
|
||||||
|
MOVW R3, R4
|
||||||
|
SUBW $1, R4, R4
|
||||||
|
|
||||||
|
CMPW $60, R4
|
||||||
|
BLT oneByte
|
||||||
|
CMPW $256, R4
|
||||||
|
BLT twoBytes
|
||||||
|
|
||||||
|
threeBytes:
|
||||||
|
MOVD $0xf4, R2
|
||||||
|
MOVB R2, 0(R8)
|
||||||
|
MOVW R4, 1(R8)
|
||||||
|
ADD $3, R8, R8
|
||||||
|
ADD $3, R6, R6
|
||||||
|
B memmove
|
||||||
|
|
||||||
|
twoBytes:
|
||||||
|
MOVD $0xf0, R2
|
||||||
|
MOVB R2, 0(R8)
|
||||||
|
MOVB R4, 1(R8)
|
||||||
|
ADD $2, R8, R8
|
||||||
|
ADD $2, R6, R6
|
||||||
|
B memmove
|
||||||
|
|
||||||
|
oneByte:
|
||||||
|
LSLW $2, R4, R4
|
||||||
|
MOVB R4, 0(R8)
|
||||||
|
ADD $1, R8, R8
|
||||||
|
ADD $1, R6, R6
|
||||||
|
|
||||||
|
memmove:
|
||||||
|
MOVD R6, ret+48(FP)
|
||||||
|
|
||||||
|
// copy(dst[i:], lit)
|
||||||
|
//
|
||||||
|
// This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push
|
||||||
|
// R8, R10 and R3 as arguments.
|
||||||
|
MOVD R8, 8(RSP)
|
||||||
|
MOVD R10, 16(RSP)
|
||||||
|
MOVD R3, 24(RSP)
|
||||||
|
CALL runtime·memmove(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// func emitCopy(dst []byte, offset, length int) int
|
||||||
|
//
|
||||||
|
// All local variables fit into registers. The register allocation:
|
||||||
|
// - R3 length
|
||||||
|
// - R7 &dst[0]
|
||||||
|
// - R8 &dst[i]
|
||||||
|
// - R11 offset
|
||||||
|
//
|
||||||
|
// The unusual register allocation of local variables, such as R11 for the
|
||||||
|
// offset, matches the allocation used at the call site in encodeBlock, which
|
||||||
|
// makes it easier to manually inline this function.
|
||||||
|
TEXT ·emitCopy(SB), NOSPLIT, $0-48
|
||||||
|
MOVD dst_base+0(FP), R8
|
||||||
|
MOVD R8, R7
|
||||||
|
MOVD offset+24(FP), R11
|
||||||
|
MOVD length+32(FP), R3
|
||||||
|
|
||||||
|
loop0:
|
||||||
|
// for length >= 68 { etc }
|
||||||
|
CMPW $68, R3
|
||||||
|
BLT step1
|
||||||
|
|
||||||
|
// Emit a length 64 copy, encoded as 3 bytes.
|
||||||
|
MOVD $0xfe, R2
|
||||||
|
MOVB R2, 0(R8)
|
||||||
|
MOVW R11, 1(R8)
|
||||||
|
ADD $3, R8, R8
|
||||||
|
SUB $64, R3, R3
|
||||||
|
B loop0
|
||||||
|
|
||||||
|
step1:
|
||||||
|
// if length > 64 { etc }
|
||||||
|
CMP $64, R3
|
||||||
|
BLE step2
|
||||||
|
|
||||||
|
// Emit a length 60 copy, encoded as 3 bytes.
|
||||||
|
MOVD $0xee, R2
|
||||||
|
MOVB R2, 0(R8)
|
||||||
|
MOVW R11, 1(R8)
|
||||||
|
ADD $3, R8, R8
|
||||||
|
SUB $60, R3, R3
|
||||||
|
|
||||||
|
step2:
|
||||||
|
// if length >= 12 || offset >= 2048 { goto step3 }
|
||||||
|
CMP $12, R3
|
||||||
|
BGE step3
|
||||||
|
CMPW $2048, R11
|
||||||
|
BGE step3
|
||||||
|
|
||||||
|
// Emit the remaining copy, encoded as 2 bytes.
|
||||||
|
MOVB R11, 1(R8)
|
||||||
|
LSRW $3, R11, R11
|
||||||
|
AND $0xe0, R11, R11
|
||||||
|
SUB $4, R3, R3
|
||||||
|
LSLW $2, R3
|
||||||
|
AND $0xff, R3, R3
|
||||||
|
ORRW R3, R11, R11
|
||||||
|
ORRW $1, R11, R11
|
||||||
|
MOVB R11, 0(R8)
|
||||||
|
ADD $2, R8, R8
|
||||||
|
|
||||||
|
// Return the number of bytes written.
|
||||||
|
SUB R7, R8, R8
|
||||||
|
MOVD R8, ret+40(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
step3:
|
||||||
|
// Emit the remaining copy, encoded as 3 bytes.
|
||||||
|
SUB $1, R3, R3
|
||||||
|
AND $0xff, R3, R3
|
||||||
|
LSLW $2, R3, R3
|
||||||
|
ORRW $2, R3, R3
|
||||||
|
MOVB R3, 0(R8)
|
||||||
|
MOVW R11, 1(R8)
|
||||||
|
ADD $3, R8, R8
|
||||||
|
|
||||||
|
// Return the number of bytes written.
|
||||||
|
SUB R7, R8, R8
|
||||||
|
MOVD R8, ret+40(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// func extendMatch(src []byte, i, j int) int
|
||||||
|
//
|
||||||
|
// All local variables fit into registers. The register allocation:
|
||||||
|
// - R6 &src[0]
|
||||||
|
// - R7 &src[j]
|
||||||
|
// - R13 &src[len(src) - 8]
|
||||||
|
// - R14 &src[len(src)]
|
||||||
|
// - R15 &src[i]
|
||||||
|
//
|
||||||
|
// The unusual register allocation of local variables, such as R15 for a source
|
||||||
|
// pointer, matches the allocation used at the call site in encodeBlock, which
|
||||||
|
// makes it easier to manually inline this function.
|
||||||
|
TEXT ·extendMatch(SB), NOSPLIT, $0-48
|
||||||
|
MOVD src_base+0(FP), R6
|
||||||
|
MOVD src_len+8(FP), R14
|
||||||
|
MOVD i+24(FP), R15
|
||||||
|
MOVD j+32(FP), R7
|
||||||
|
ADD R6, R14, R14
|
||||||
|
ADD R6, R15, R15
|
||||||
|
ADD R6, R7, R7
|
||||||
|
MOVD R14, R13
|
||||||
|
SUB $8, R13, R13
|
||||||
|
|
||||||
|
cmp8:
|
||||||
|
// As long as we are 8 or more bytes before the end of src, we can load and
|
||||||
|
// compare 8 bytes at a time. If those 8 bytes are equal, repeat.
|
||||||
|
CMP R13, R7
|
||||||
|
BHI cmp1
|
||||||
|
MOVD (R15), R3
|
||||||
|
MOVD (R7), R4
|
||||||
|
CMP R4, R3
|
||||||
|
BNE bsf
|
||||||
|
ADD $8, R15, R15
|
||||||
|
ADD $8, R7, R7
|
||||||
|
B cmp8
|
||||||
|
|
||||||
|
bsf:
|
||||||
|
// If those 8 bytes were not equal, XOR the two 8 byte values, and return
|
||||||
|
// the index of the first byte that differs.
|
||||||
|
// RBIT reverses the bit order, then CLZ counts the leading zeros, the
|
||||||
|
// combination of which finds the least significant bit which is set.
|
||||||
|
// The arm64 architecture is little-endian, and the shift by 3 converts
|
||||||
|
// a bit index to a byte index.
|
||||||
|
EOR R3, R4, R4
|
||||||
|
RBIT R4, R4
|
||||||
|
CLZ R4, R4
|
||||||
|
ADD R4>>3, R7, R7
|
||||||
|
|
||||||
|
// Convert from &src[ret] to ret.
|
||||||
|
SUB R6, R7, R7
|
||||||
|
MOVD R7, ret+40(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
cmp1:
|
||||||
|
// In src's tail, compare 1 byte at a time.
|
||||||
|
CMP R7, R14
|
||||||
|
BLS extendMatchEnd
|
||||||
|
MOVB (R15), R3
|
||||||
|
MOVB (R7), R4
|
||||||
|
CMP R4, R3
|
||||||
|
BNE extendMatchEnd
|
||||||
|
ADD $1, R15, R15
|
||||||
|
ADD $1, R7, R7
|
||||||
|
B cmp1
|
||||||
|
|
||||||
|
extendMatchEnd:
|
||||||
|
// Convert from &src[ret] to ret.
|
||||||
|
SUB R6, R7, R7
|
||||||
|
MOVD R7, ret+40(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// func encodeBlock(dst, src []byte) (d int)
|
||||||
|
//
|
||||||
|
// All local variables fit into registers, other than "var table". The register
|
||||||
|
// allocation:
|
||||||
|
// - R3 . .
|
||||||
|
// - R4 . .
|
||||||
|
// - R5 64 shift
|
||||||
|
// - R6 72 &src[0], tableSize
|
||||||
|
// - R7 80 &src[s]
|
||||||
|
// - R8 88 &dst[d]
|
||||||
|
// - R9 96 sLimit
|
||||||
|
// - R10 . &src[nextEmit]
|
||||||
|
// - R11 104 prevHash, currHash, nextHash, offset
|
||||||
|
// - R12 112 &src[base], skip
|
||||||
|
// - R13 . &src[nextS], &src[len(src) - 8]
|
||||||
|
// - R14 . len(src), bytesBetweenHashLookups, &src[len(src)], x
|
||||||
|
// - R15 120 candidate
|
||||||
|
// - R16 . hash constant, 0x1e35a7bd
|
||||||
|
// - R17 . &table
|
||||||
|
// - . 128 table
|
||||||
|
//
|
||||||
|
// The second column (64, 72, etc) is the stack offset to spill the registers
|
||||||
|
// when calling other functions. We could pack this slightly tighter, but it's
|
||||||
|
// simpler to have a dedicated spill map independent of the function called.
|
||||||
|
//
|
||||||
|
// "var table [maxTableSize]uint16" takes up 32768 bytes of stack space. An
|
||||||
|
// extra 64 bytes, to call other functions, and an extra 64 bytes, to spill
|
||||||
|
// local variables (registers) during calls gives 32768 + 64 + 64 = 32896.
|
||||||
|
TEXT ·encodeBlock(SB), 0, $32896-56
|
||||||
|
MOVD dst_base+0(FP), R8
|
||||||
|
MOVD src_base+24(FP), R7
|
||||||
|
MOVD src_len+32(FP), R14
|
||||||
|
|
||||||
|
// shift, tableSize := uint32(32-8), 1<<8
|
||||||
|
MOVD $24, R5
|
||||||
|
MOVD $256, R6
|
||||||
|
MOVW $0xa7bd, R16
|
||||||
|
MOVKW $(0x1e35<<16), R16
|
||||||
|
|
||||||
|
calcShift:
|
||||||
|
// for ; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 {
|
||||||
|
// shift--
|
||||||
|
// }
|
||||||
|
MOVD $16384, R2
|
||||||
|
CMP R2, R6
|
||||||
|
BGE varTable
|
||||||
|
CMP R14, R6
|
||||||
|
BGE varTable
|
||||||
|
SUB $1, R5, R5
|
||||||
|
LSL $1, R6, R6
|
||||||
|
B calcShift
|
||||||
|
|
||||||
|
varTable:
|
||||||
|
// var table [maxTableSize]uint16
|
||||||
|
//
|
||||||
|
// In the asm code, unlike the Go code, we can zero-initialize only the
|
||||||
|
// first tableSize elements. Each uint16 element is 2 bytes and each
|
||||||
|
// iterations writes 64 bytes, so we can do only tableSize/32 writes
|
||||||
|
// instead of the 2048 writes that would zero-initialize all of table's
|
||||||
|
// 32768 bytes. This clear could overrun the first tableSize elements, but
|
||||||
|
// it won't overrun the allocated stack size.
|
||||||
|
ADD $128, RSP, R17
|
||||||
|
MOVD R17, R4
|
||||||
|
|
||||||
|
// !!! R6 = &src[tableSize]
|
||||||
|
ADD R6<<1, R17, R6
|
||||||
|
|
||||||
|
memclr:
|
||||||
|
STP.P (ZR, ZR), 64(R4)
|
||||||
|
STP (ZR, ZR), -48(R4)
|
||||||
|
STP (ZR, ZR), -32(R4)
|
||||||
|
STP (ZR, ZR), -16(R4)
|
||||||
|
CMP R4, R6
|
||||||
|
BHI memclr
|
||||||
|
|
||||||
|
// !!! R6 = &src[0]
|
||||||
|
MOVD R7, R6
|
||||||
|
|
||||||
|
// sLimit := len(src) - inputMargin
|
||||||
|
MOVD R14, R9
|
||||||
|
SUB $15, R9, R9
|
||||||
|
|
||||||
|
// !!! Pre-emptively spill R5, R6 and R9 to the stack. Their values don't
|
||||||
|
// change for the rest of the function.
|
||||||
|
MOVD R5, 64(RSP)
|
||||||
|
MOVD R6, 72(RSP)
|
||||||
|
MOVD R9, 96(RSP)
|
||||||
|
|
||||||
|
// nextEmit := 0
|
||||||
|
MOVD R6, R10
|
||||||
|
|
||||||
|
// s := 1
|
||||||
|
ADD $1, R7, R7
|
||||||
|
|
||||||
|
// nextHash := hash(load32(src, s), shift)
|
||||||
|
MOVW 0(R7), R11
|
||||||
|
MULW R16, R11, R11
|
||||||
|
LSRW R5, R11, R11
|
||||||
|
|
||||||
|
outer:
|
||||||
|
// for { etc }
|
||||||
|
|
||||||
|
// skip := 32
|
||||||
|
MOVD $32, R12
|
||||||
|
|
||||||
|
// nextS := s
|
||||||
|
MOVD R7, R13
|
||||||
|
|
||||||
|
// candidate := 0
|
||||||
|
MOVD $0, R15
|
||||||
|
|
||||||
|
inner0:
|
||||||
|
// for { etc }
|
||||||
|
|
||||||
|
// s := nextS
|
||||||
|
MOVD R13, R7
|
||||||
|
|
||||||
|
// bytesBetweenHashLookups := skip >> 5
|
||||||
|
MOVD R12, R14
|
||||||
|
LSR $5, R14, R14
|
||||||
|
|
||||||
|
// nextS = s + bytesBetweenHashLookups
|
||||||
|
ADD R14, R13, R13
|
||||||
|
|
||||||
|
// skip += bytesBetweenHashLookups
|
||||||
|
ADD R14, R12, R12
|
||||||
|
|
||||||
|
// if nextS > sLimit { goto emitRemainder }
|
||||||
|
MOVD R13, R3
|
||||||
|
SUB R6, R3, R3
|
||||||
|
CMP R9, R3
|
||||||
|
BHI emitRemainder
|
||||||
|
|
||||||
|
// candidate = int(table[nextHash])
|
||||||
|
MOVHU 0(R17)(R11<<1), R15
|
||||||
|
|
||||||
|
// table[nextHash] = uint16(s)
|
||||||
|
MOVD R7, R3
|
||||||
|
SUB R6, R3, R3
|
||||||
|
|
||||||
|
MOVH R3, 0(R17)(R11<<1)
|
||||||
|
|
||||||
|
// nextHash = hash(load32(src, nextS), shift)
|
||||||
|
MOVW 0(R13), R11
|
||||||
|
MULW R16, R11
|
||||||
|
LSRW R5, R11, R11
|
||||||
|
|
||||||
|
// if load32(src, s) != load32(src, candidate) { continue } break
|
||||||
|
MOVW 0(R7), R3
|
||||||
|
MOVW (R6)(R15*1), R4
|
||||||
|
CMPW R4, R3
|
||||||
|
BNE inner0
|
||||||
|
|
||||||
|
fourByteMatch:
|
||||||
|
// As per the encode_other.go code:
|
||||||
|
//
|
||||||
|
// A 4-byte match has been found. We'll later see etc.
|
||||||
|
|
||||||
|
// !!! Jump to a fast path for short (<= 16 byte) literals. See the comment
|
||||||
|
// on inputMargin in encode.go.
|
||||||
|
MOVD R7, R3
|
||||||
|
SUB R10, R3, R3
|
||||||
|
CMP $16, R3
|
||||||
|
BLE emitLiteralFastPath
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// Begin inline of the emitLiteral call.
|
||||||
|
//
|
||||||
|
// d += emitLiteral(dst[d:], src[nextEmit:s])
|
||||||
|
|
||||||
|
MOVW R3, R4
|
||||||
|
SUBW $1, R4, R4
|
||||||
|
|
||||||
|
MOVW $60, R2
|
||||||
|
CMPW R2, R4
|
||||||
|
BLT inlineEmitLiteralOneByte
|
||||||
|
MOVW $256, R2
|
||||||
|
CMPW R2, R4
|
||||||
|
BLT inlineEmitLiteralTwoBytes
|
||||||
|
|
||||||
|
inlineEmitLiteralThreeBytes:
|
||||||
|
MOVD $0xf4, R1
|
||||||
|
MOVB R1, 0(R8)
|
||||||
|
MOVW R4, 1(R8)
|
||||||
|
ADD $3, R8, R8
|
||||||
|
B inlineEmitLiteralMemmove
|
||||||
|
|
||||||
|
inlineEmitLiteralTwoBytes:
|
||||||
|
MOVD $0xf0, R1
|
||||||
|
MOVB R1, 0(R8)
|
||||||
|
MOVB R4, 1(R8)
|
||||||
|
ADD $2, R8, R8
|
||||||
|
B inlineEmitLiteralMemmove
|
||||||
|
|
||||||
|
inlineEmitLiteralOneByte:
|
||||||
|
LSLW $2, R4, R4
|
||||||
|
MOVB R4, 0(R8)
|
||||||
|
ADD $1, R8, R8
|
||||||
|
|
||||||
|
inlineEmitLiteralMemmove:
|
||||||
|
// Spill local variables (registers) onto the stack; call; unspill.
|
||||||
|
//
|
||||||
|
// copy(dst[i:], lit)
|
||||||
|
//
|
||||||
|
// This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push
|
||||||
|
// R8, R10 and R3 as arguments.
|
||||||
|
MOVD R8, 8(RSP)
|
||||||
|
MOVD R10, 16(RSP)
|
||||||
|
MOVD R3, 24(RSP)
|
||||||
|
|
||||||
|
// Finish the "d +=" part of "d += emitLiteral(etc)".
|
||||||
|
ADD R3, R8, R8
|
||||||
|
MOVD R7, 80(RSP)
|
||||||
|
MOVD R8, 88(RSP)
|
||||||
|
MOVD R15, 120(RSP)
|
||||||
|
CALL runtime·memmove(SB)
|
||||||
|
MOVD 64(RSP), R5
|
||||||
|
MOVD 72(RSP), R6
|
||||||
|
MOVD 80(RSP), R7
|
||||||
|
MOVD 88(RSP), R8
|
||||||
|
MOVD 96(RSP), R9
|
||||||
|
MOVD 120(RSP), R15
|
||||||
|
ADD $128, RSP, R17
|
||||||
|
MOVW $0xa7bd, R16
|
||||||
|
MOVKW $(0x1e35<<16), R16
|
||||||
|
B inner1
|
||||||
|
|
||||||
|
inlineEmitLiteralEnd:
|
||||||
|
// End inline of the emitLiteral call.
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
emitLiteralFastPath:
|
||||||
|
// !!! Emit the 1-byte encoding "uint8(len(lit)-1)<<2".
|
||||||
|
MOVB R3, R4
|
||||||
|
SUBW $1, R4, R4
|
||||||
|
AND $0xff, R4, R4
|
||||||
|
LSLW $2, R4, R4
|
||||||
|
MOVB R4, (R8)
|
||||||
|
ADD $1, R8, R8
|
||||||
|
|
||||||
|
// !!! Implement the copy from lit to dst as a 16-byte load and store.
|
||||||
|
// (Encode's documentation says that dst and src must not overlap.)
|
||||||
|
//
|
||||||
|
// This always copies 16 bytes, instead of only len(lit) bytes, but that's
|
||||||
|
// OK. Subsequent iterations will fix up the overrun.
|
||||||
|
//
|
||||||
|
// Note that on arm64, it is legal and cheap to issue unaligned 8-byte or
|
||||||
|
// 16-byte loads and stores. This technique probably wouldn't be as
|
||||||
|
// effective on architectures that are fussier about alignment.
|
||||||
|
LDP 0(R10), (R0, R1)
|
||||||
|
STP (R0, R1), 0(R8)
|
||||||
|
ADD R3, R8, R8
|
||||||
|
|
||||||
|
inner1:
|
||||||
|
// for { etc }
|
||||||
|
|
||||||
|
// base := s
|
||||||
|
MOVD R7, R12
|
||||||
|
|
||||||
|
// !!! offset := base - candidate
|
||||||
|
MOVD R12, R11
|
||||||
|
SUB R15, R11, R11
|
||||||
|
SUB R6, R11, R11
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// Begin inline of the extendMatch call.
|
||||||
|
//
|
||||||
|
// s = extendMatch(src, candidate+4, s+4)
|
||||||
|
|
||||||
|
// !!! R14 = &src[len(src)]
|
||||||
|
MOVD src_len+32(FP), R14
|
||||||
|
ADD R6, R14, R14
|
||||||
|
|
||||||
|
// !!! R13 = &src[len(src) - 8]
|
||||||
|
MOVD R14, R13
|
||||||
|
SUB $8, R13, R13
|
||||||
|
|
||||||
|
// !!! R15 = &src[candidate + 4]
|
||||||
|
ADD $4, R15, R15
|
||||||
|
ADD R6, R15, R15
|
||||||
|
|
||||||
|
// !!! s += 4
|
||||||
|
ADD $4, R7, R7
|
||||||
|
|
||||||
|
inlineExtendMatchCmp8:
|
||||||
|
// As long as we are 8 or more bytes before the end of src, we can load and
|
||||||
|
// compare 8 bytes at a time. If those 8 bytes are equal, repeat.
|
||||||
|
CMP R13, R7
|
||||||
|
BHI inlineExtendMatchCmp1
|
||||||
|
MOVD (R15), R3
|
||||||
|
MOVD (R7), R4
|
||||||
|
CMP R4, R3
|
||||||
|
BNE inlineExtendMatchBSF
|
||||||
|
ADD $8, R15, R15
|
||||||
|
ADD $8, R7, R7
|
||||||
|
B inlineExtendMatchCmp8
|
||||||
|
|
||||||
|
inlineExtendMatchBSF:
|
||||||
|
// If those 8 bytes were not equal, XOR the two 8 byte values, and return
|
||||||
|
// the index of the first byte that differs.
|
||||||
|
// RBIT reverses the bit order, then CLZ counts the leading zeros, the
|
||||||
|
// combination of which finds the least significant bit which is set.
|
||||||
|
// The arm64 architecture is little-endian, and the shift by 3 converts
|
||||||
|
// a bit index to a byte index.
|
||||||
|
EOR R3, R4, R4
|
||||||
|
RBIT R4, R4
|
||||||
|
CLZ R4, R4
|
||||||
|
ADD R4>>3, R7, R7
|
||||||
|
B inlineExtendMatchEnd
|
||||||
|
|
||||||
|
inlineExtendMatchCmp1:
|
||||||
|
// In src's tail, compare 1 byte at a time.
|
||||||
|
CMP R7, R14
|
||||||
|
BLS inlineExtendMatchEnd
|
||||||
|
MOVB (R15), R3
|
||||||
|
MOVB (R7), R4
|
||||||
|
CMP R4, R3
|
||||||
|
BNE inlineExtendMatchEnd
|
||||||
|
ADD $1, R15, R15
|
||||||
|
ADD $1, R7, R7
|
||||||
|
B inlineExtendMatchCmp1
|
||||||
|
|
||||||
|
inlineExtendMatchEnd:
|
||||||
|
// End inline of the extendMatch call.
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// Begin inline of the emitCopy call.
|
||||||
|
//
|
||||||
|
// d += emitCopy(dst[d:], base-candidate, s-base)
|
||||||
|
|
||||||
|
// !!! length := s - base
|
||||||
|
MOVD R7, R3
|
||||||
|
SUB R12, R3, R3
|
||||||
|
|
||||||
|
inlineEmitCopyLoop0:
|
||||||
|
// for length >= 68 { etc }
|
||||||
|
MOVW $68, R2
|
||||||
|
CMPW R2, R3
|
||||||
|
BLT inlineEmitCopyStep1
|
||||||
|
|
||||||
|
// Emit a length 64 copy, encoded as 3 bytes.
|
||||||
|
MOVD $0xfe, R1
|
||||||
|
MOVB R1, 0(R8)
|
||||||
|
MOVW R11, 1(R8)
|
||||||
|
ADD $3, R8, R8
|
||||||
|
SUBW $64, R3, R3
|
||||||
|
B inlineEmitCopyLoop0
|
||||||
|
|
||||||
|
inlineEmitCopyStep1:
|
||||||
|
// if length > 64 { etc }
|
||||||
|
MOVW $64, R2
|
||||||
|
CMPW R2, R3
|
||||||
|
BLE inlineEmitCopyStep2
|
||||||
|
|
||||||
|
// Emit a length 60 copy, encoded as 3 bytes.
|
||||||
|
MOVD $0xee, R1
|
||||||
|
MOVB R1, 0(R8)
|
||||||
|
MOVW R11, 1(R8)
|
||||||
|
ADD $3, R8, R8
|
||||||
|
SUBW $60, R3, R3
|
||||||
|
|
||||||
|
inlineEmitCopyStep2:
|
||||||
|
// if length >= 12 || offset >= 2048 { goto inlineEmitCopyStep3 }
|
||||||
|
MOVW $12, R2
|
||||||
|
CMPW R2, R3
|
||||||
|
BGE inlineEmitCopyStep3
|
||||||
|
MOVW $2048, R2
|
||||||
|
CMPW R2, R11
|
||||||
|
BGE inlineEmitCopyStep3
|
||||||
|
|
||||||
|
// Emit the remaining copy, encoded as 2 bytes.
|
||||||
|
MOVB R11, 1(R8)
|
||||||
|
LSRW $8, R11, R11
|
||||||
|
LSLW $5, R11, R11
|
||||||
|
SUBW $4, R3, R3
|
||||||
|
AND $0xff, R3, R3
|
||||||
|
LSLW $2, R3, R3
|
||||||
|
ORRW R3, R11, R11
|
||||||
|
ORRW $1, R11, R11
|
||||||
|
MOVB R11, 0(R8)
|
||||||
|
ADD $2, R8, R8
|
||||||
|
B inlineEmitCopyEnd
|
||||||
|
|
||||||
|
inlineEmitCopyStep3:
|
||||||
|
// Emit the remaining copy, encoded as 3 bytes.
|
||||||
|
SUBW $1, R3, R3
|
||||||
|
LSLW $2, R3, R3
|
||||||
|
ORRW $2, R3, R3
|
||||||
|
MOVB R3, 0(R8)
|
||||||
|
MOVW R11, 1(R8)
|
||||||
|
ADD $3, R8, R8
|
||||||
|
|
||||||
|
inlineEmitCopyEnd:
|
||||||
|
// End inline of the emitCopy call.
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
// nextEmit = s
|
||||||
|
MOVD R7, R10
|
||||||
|
|
||||||
|
// if s >= sLimit { goto emitRemainder }
|
||||||
|
MOVD R7, R3
|
||||||
|
SUB R6, R3, R3
|
||||||
|
CMP R3, R9
|
||||||
|
BLS emitRemainder
|
||||||
|
|
||||||
|
// As per the encode_other.go code:
|
||||||
|
//
|
||||||
|
// We could immediately etc.
|
||||||
|
|
||||||
|
// x := load64(src, s-1)
|
||||||
|
MOVD -1(R7), R14
|
||||||
|
|
||||||
|
// prevHash := hash(uint32(x>>0), shift)
|
||||||
|
MOVW R14, R11
|
||||||
|
MULW R16, R11, R11
|
||||||
|
LSRW R5, R11, R11
|
||||||
|
|
||||||
|
// table[prevHash] = uint16(s-1)
|
||||||
|
MOVD R7, R3
|
||||||
|
SUB R6, R3, R3
|
||||||
|
SUB $1, R3, R3
|
||||||
|
|
||||||
|
MOVHU R3, 0(R17)(R11<<1)
|
||||||
|
|
||||||
|
// currHash := hash(uint32(x>>8), shift)
|
||||||
|
LSR $8, R14, R14
|
||||||
|
MOVW R14, R11
|
||||||
|
MULW R16, R11, R11
|
||||||
|
LSRW R5, R11, R11
|
||||||
|
|
||||||
|
// candidate = int(table[currHash])
|
||||||
|
MOVHU 0(R17)(R11<<1), R15
|
||||||
|
|
||||||
|
// table[currHash] = uint16(s)
|
||||||
|
ADD $1, R3, R3
|
||||||
|
MOVHU R3, 0(R17)(R11<<1)
|
||||||
|
|
||||||
|
// if uint32(x>>8) == load32(src, candidate) { continue }
|
||||||
|
MOVW (R6)(R15*1), R4
|
||||||
|
CMPW R4, R14
|
||||||
|
BEQ inner1
|
||||||
|
|
||||||
|
// nextHash = hash(uint32(x>>16), shift)
|
||||||
|
LSR $8, R14, R14
|
||||||
|
MOVW R14, R11
|
||||||
|
MULW R16, R11, R11
|
||||||
|
LSRW R5, R11, R11
|
||||||
|
|
||||||
|
// s++
|
||||||
|
ADD $1, R7, R7
|
||||||
|
|
||||||
|
// break out of the inner1 for loop, i.e. continue the outer loop.
|
||||||
|
B outer
|
||||||
|
|
||||||
|
emitRemainder:
|
||||||
|
// if nextEmit < len(src) { etc }
|
||||||
|
MOVD src_len+32(FP), R3
|
||||||
|
ADD R6, R3, R3
|
||||||
|
CMP R3, R10
|
||||||
|
BEQ encodeBlockEnd
|
||||||
|
|
||||||
|
// d += emitLiteral(dst[d:], src[nextEmit:])
|
||||||
|
//
|
||||||
|
// Push args.
|
||||||
|
MOVD R8, 8(RSP)
|
||||||
|
MOVD $0, 16(RSP) // Unnecessary, as the callee ignores it, but conservative.
|
||||||
|
MOVD $0, 24(RSP) // Unnecessary, as the callee ignores it, but conservative.
|
||||||
|
MOVD R10, 32(RSP)
|
||||||
|
SUB R10, R3, R3
|
||||||
|
MOVD R3, 40(RSP)
|
||||||
|
MOVD R3, 48(RSP) // Unnecessary, as the callee ignores it, but conservative.
|
||||||
|
|
||||||
|
// Spill local variables (registers) onto the stack; call; unspill.
|
||||||
|
MOVD R8, 88(RSP)
|
||||||
|
CALL ·emitLiteral(SB)
|
||||||
|
MOVD 88(RSP), R8
|
||||||
|
|
||||||
|
// Finish the "d +=" part of "d += emitLiteral(etc)".
|
||||||
|
MOVD 56(RSP), R1
|
||||||
|
ADD R1, R8, R8
|
||||||
|
|
||||||
|
encodeBlockEnd:
|
||||||
|
MOVD dst_base+0(FP), R3
|
||||||
|
SUB R3, R8, R8
|
||||||
|
MOVD R8, d+48(FP)
|
||||||
|
RET
|
@ -5,6 +5,7 @@
|
|||||||
// +build !appengine
|
// +build !appengine
|
||||||
// +build gc
|
// +build gc
|
||||||
// +build !noasm
|
// +build !noasm
|
||||||
|
// +build amd64 arm64
|
||||||
|
|
||||||
package snappy
|
package snappy
|
||||||
|
|
2
vendor/github.com/golang/snappy/encode_other.go
generated
vendored
2
vendor/github.com/golang/snappy/encode_other.go
generated
vendored
@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build !amd64 appengine !gc noasm
|
// +build !amd64,!arm64 appengine !gc noasm
|
||||||
|
|
||||||
package snappy
|
package snappy
|
||||||
|
|
||||||
|
1
vendor/github.com/golang/snappy/go.mod
generated
vendored
Normal file
1
vendor/github.com/golang/snappy/go.mod
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
module github.com/golang/snappy
|
1
vendor/github.com/labstack/echo/v4/.gitignore
generated
vendored
1
vendor/github.com/labstack/echo/v4/.gitignore
generated
vendored
@ -5,3 +5,4 @@ vendor
|
|||||||
.idea
|
.idea
|
||||||
*.iml
|
*.iml
|
||||||
*.out
|
*.out
|
||||||
|
.vscode
|
||||||
|
8
vendor/github.com/labstack/echo/v4/.travis.yml
generated
vendored
8
vendor/github.com/labstack/echo/v4/.travis.yml
generated
vendored
@ -1,7 +1,11 @@
|
|||||||
|
arch:
|
||||||
|
- amd64
|
||||||
|
- ppc64le
|
||||||
|
|
||||||
language: go
|
language: go
|
||||||
go:
|
go:
|
||||||
- 1.12.x
|
- 1.14.x
|
||||||
- 1.13.x
|
- 1.15.x
|
||||||
- tip
|
- tip
|
||||||
env:
|
env:
|
||||||
- GO111MODULE=on
|
- GO111MODULE=on
|
||||||
|
95
vendor/github.com/labstack/echo/v4/CHANGELOG.md
generated
vendored
Normal file
95
vendor/github.com/labstack/echo/v4/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## v4.2.1 - 2020-03-08
|
||||||
|
|
||||||
|
**Important notes**
|
||||||
|
|
||||||
|
Due to a datarace the config parameters for the newly added timeout middleware required a change.
|
||||||
|
See the [docs](https://echo.labstack.com/middleware/timeout).
|
||||||
|
A performance regression has been fixed, even bringing better performance than before for some routing scenarios.
|
||||||
|
|
||||||
|
**Fixes**
|
||||||
|
|
||||||
|
* Fix performance regression caused by path escaping (#1777, #1798, #1799, aldas)
|
||||||
|
* Avoid context canceled errors (#1789, clwluvw)
|
||||||
|
* Improve router to use on stack backtracking (#1791, aldas, stffabi)
|
||||||
|
* Fix panic in timeout middleware not being not recovered and cause application crash (#1794, aldas)
|
||||||
|
* Fix Echo.Serve() not serving on HTTP port correctly when TLSListener is used (#1785, #1793, aldas)
|
||||||
|
* Apply go fmt (#1788, Le0tk0k)
|
||||||
|
* Uses strings.Equalfold (#1790, rkilingr)
|
||||||
|
* Improve code quality (#1792, withshubh)
|
||||||
|
|
||||||
|
This release was made possible by our **contributors**:
|
||||||
|
aldas, clwluvw, lammel, Le0tk0k, maciej-jezierski, rkilingr, stffabi, withshubh
|
||||||
|
|
||||||
|
## v4.2.0 - 2020-02-11
|
||||||
|
|
||||||
|
**Important notes**
|
||||||
|
|
||||||
|
The behaviour for binding data has been reworked for compatibility with echo before v4.1.11 by
|
||||||
|
enforcing `explicit tagging` for processing parameters. This **may break** your code if you
|
||||||
|
expect combined handling of query/path/form params.
|
||||||
|
Please see the updated documentation for [request](https://echo.labstack.com/guide/request) and [binding](https://echo.labstack.com/guide/request)
|
||||||
|
|
||||||
|
The handling for rewrite rules has been slightly adjusted to expand `*` to a non-greedy `(.*?)` capture group. This is only relevant if multiple asterisks are used in your rules.
|
||||||
|
Please see [rewrite](https://echo.labstack.com/middleware/rewrite) and [proxy](https://echo.labstack.com/middleware/proxy) for details.
|
||||||
|
|
||||||
|
**Security**
|
||||||
|
|
||||||
|
* Fix directory traversal vulnerability for Windows (#1718, little-cui)
|
||||||
|
* Fix open redirect vulnerability with trailing slash (#1771,#1775 aldas,GeoffreyFrogeye)
|
||||||
|
|
||||||
|
**Enhancements**
|
||||||
|
|
||||||
|
* Add Echo#ListenerNetwork as configuration (#1667, pafuent)
|
||||||
|
* Add ability to change the status code using response beforeFuncs (#1706, RashadAnsari)
|
||||||
|
* Echo server startup to allow data race free access to listener address
|
||||||
|
* Binder: Restore pre v4.1.11 behaviour for c.Bind() to use query params only for GET or DELETE methods (#1727, aldas)
|
||||||
|
* Binder: Add separate methods to bind only query params, path params or request body (#1681, aldas)
|
||||||
|
* Binder: New fluent binder for query/path/form parameter binding (#1717, #1736, aldas)
|
||||||
|
* Router: Performance improvements for missed routes (#1689, pafuent)
|
||||||
|
* Router: Improve performance for Real-IP detection using IndexByte instead of Split (#1640, imxyb)
|
||||||
|
* Middleware: Support real regex rules for rewrite and proxy middleware (#1767)
|
||||||
|
* Middleware: New rate limiting middleware (#1724, iambenkay)
|
||||||
|
* Middleware: New timeout middleware implementation for go1.13+ (#1743, )
|
||||||
|
* Middleware: Allow regex pattern for CORS middleware (#1623, KlotzAndrew)
|
||||||
|
* Middleware: Add IgnoreBase parameter to static middleware (#1701, lnenad, iambenkay)
|
||||||
|
* Middleware: Add an optional custom function to CORS middleware to validate origin (#1651, curvegrid)
|
||||||
|
* Middleware: Support form fields in JWT middleware (#1704, rkfg)
|
||||||
|
* Middleware: Use sync.Pool for (de)compress middleware to improve performance (#1699, #1672, pafuent)
|
||||||
|
* Middleware: Add decompress middleware to support gzip compressed requests (#1687, arun0009)
|
||||||
|
* Middleware: Add ErrJWTInvalid for JWT middleware (#1627, juanbelieni)
|
||||||
|
* Middleware: Add SameSite mode for CSRF cookies to support iframes (#1524, pr0head)
|
||||||
|
|
||||||
|
**Fixes**
|
||||||
|
|
||||||
|
* Fix handling of special trailing slash case for partial prefix (#1741, stffabi)
|
||||||
|
* Fix handling of static routes with trailing slash (#1747)
|
||||||
|
* Fix Static files route not working (#1671, pwli0755, lammel)
|
||||||
|
* Fix use of caret(^) in regex for rewrite middleware (#1588, chotow)
|
||||||
|
* Fix Echo#Reverse for Any type routes (#1695, pafuent)
|
||||||
|
* Fix Router#Find panic with infinite loop (#1661, pafuent)
|
||||||
|
* Fix Router#Find panic fails on Param paths (#1659, pafuent)
|
||||||
|
* Fix DefaultHTTPErrorHandler with Debug=true (#1477, lammel)
|
||||||
|
* Fix incorrect CORS headers (#1669, ulasakdeniz)
|
||||||
|
* Fix proxy middleware rewritePath to use url with updated tests (#1630, arun0009)
|
||||||
|
* Fix rewritePath for proxy middleware to use escaped path in (#1628, arun0009)
|
||||||
|
* Remove unless defer (#1656, imxyb)
|
||||||
|
|
||||||
|
**General**
|
||||||
|
|
||||||
|
* New maintainers for Echo: Roland Lammel (@lammel) and Pablo Andres Fuente (@pafuent)
|
||||||
|
* Add GitHub action to compare benchmarks (#1702, pafuent)
|
||||||
|
* Binding query/path params and form fields to struct only works for explicit tags (#1729,#1734, aldas)
|
||||||
|
* Add support for Go 1.15 in CI (#1683, asahasrabuddhe)
|
||||||
|
* Add test for request id to remain unchanged if provided (#1719, iambenkay)
|
||||||
|
* Refactor echo instance listener access and startup to speed up testing (#1735, aldas)
|
||||||
|
* Refactor and improve various tests for binding and routing
|
||||||
|
* Run test workflow only for relevant changes (#1637, #1636, pofl)
|
||||||
|
* Update .travis.yml (#1662, santosh653)
|
||||||
|
* Update README.md with an recents framework benchmark (#1679, pafuent)
|
||||||
|
|
||||||
|
This release was made possible by **over 100 commits** from more than **20 contributors**:
|
||||||
|
asahasrabuddhe, aldas, AndrewKlotz, arun0009, chotow, curvegrid, iambenkay, imxyb,
|
||||||
|
juanbelieni, lammel, little-cui, lnenad, pafuent, pofl, pr0head, pwli, RashadAnsari,
|
||||||
|
rkfg, santosh653, segfiner, stffabi, ulasakdeniz
|
31
vendor/github.com/labstack/echo/v4/Makefile
generated
vendored
31
vendor/github.com/labstack/echo/v4/Makefile
generated
vendored
@ -1,3 +1,34 @@
|
|||||||
|
PKG := "github.com/labstack/echo"
|
||||||
|
PKG_LIST := $(shell go list ${PKG}/...)
|
||||||
|
|
||||||
tag:
|
tag:
|
||||||
@git tag `grep -P '^\tversion = ' echo.go|cut -f2 -d'"'`
|
@git tag `grep -P '^\tversion = ' echo.go|cut -f2 -d'"'`
|
||||||
@git tag|grep -v ^v
|
@git tag|grep -v ^v
|
||||||
|
|
||||||
|
.DEFAULT_GOAL := check
|
||||||
|
check: lint vet race ## Check project
|
||||||
|
|
||||||
|
init:
|
||||||
|
@go get -u golang.org/x/lint/golint
|
||||||
|
|
||||||
|
lint: ## Lint the files
|
||||||
|
@golint -set_exit_status ${PKG_LIST}
|
||||||
|
|
||||||
|
vet: ## Vet the files
|
||||||
|
@go vet ${PKG_LIST}
|
||||||
|
|
||||||
|
test: ## Run tests
|
||||||
|
@go test -short ${PKG_LIST}
|
||||||
|
|
||||||
|
race: ## Run tests with data race detector
|
||||||
|
@go test -race ${PKG_LIST}
|
||||||
|
|
||||||
|
benchmark: ## Run benchmarks
|
||||||
|
@go test -run="-" -bench=".*" ${PKG_LIST}
|
||||||
|
|
||||||
|
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}'
|
||||||
|
|
||||||
|
goversion ?= "1.15"
|
||||||
|
test_version: ## Run tests inside Docker with given version (defaults to 1.15 oldest supported). Example: make test_version goversion=1.15
|
||||||
|
@docker run --rm -it -v $(shell pwd):/project golang:$(goversion) /bin/sh -c "cd /project && make init check"
|
||||||
|
17
vendor/github.com/labstack/echo/v4/README.md
generated
vendored
17
vendor/github.com/labstack/echo/v4/README.md
generated
vendored
@ -1,12 +1,12 @@
|
|||||||
<a href="https://echo.labstack.com"><img height="80" src="https://cdn.labstack.com/images/echo-logo.svg"></a>
|
<a href="https://echo.labstack.com"><img height="80" src="https://cdn.labstack.com/images/echo-logo.svg"></a>
|
||||||
|
|
||||||
[![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)](http://godoc.org/github.com/labstack/echo)
|
[![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)
|
[![Build Status](http://img.shields.io/travis/labstack/echo.svg?style=flat-square)](https://travis-ci.org/labstack/echo)
|
||||||
[![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)
|
||||||
[![Join the chat at https://gitter.im/labstack/echo](https://img.shields.io/badge/gitter-join%20chat-brightgreen.svg?style=flat-square)](https://gitter.im/labstack/echo)
|
[![Join the chat at https://gitter.im/labstack/echo](https://img.shields.io/badge/gitter-join%20chat-brightgreen.svg?style=flat-square)](https://gitter.im/labstack/echo)
|
||||||
[![Forum](https://img.shields.io/badge/community-forum-00afd1.svg?style=flat-square)](https://forum.labstack.com)
|
[![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)
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ Therefore a Go version capable of understanding /vN suffixed imports is required
|
|||||||
|
|
||||||
- 1.9.7+
|
- 1.9.7+
|
||||||
- 1.10.3+
|
- 1.10.3+
|
||||||
- 1.11+
|
- 1.14+
|
||||||
|
|
||||||
Any of these versions will allow you to import Echo as `github.com/labstack/echo/v4` which is the recommended
|
Any of these versions will allow you to import Echo as `github.com/labstack/echo/v4` which is the recommended
|
||||||
way of using Echo going forward.
|
way of using Echo going forward.
|
||||||
@ -42,17 +42,20 @@ For older versions, please use the latest v3 tag.
|
|||||||
|
|
||||||
## Benchmarks
|
## Benchmarks
|
||||||
|
|
||||||
Date: 2018/03/15<br>
|
Date: 2020/11/11<br>
|
||||||
Source: https://github.com/vishr/web-framework-benchmark<br>
|
Source: https://github.com/vishr/web-framework-benchmark<br>
|
||||||
Lower is better!
|
Lower is better!
|
||||||
|
|
||||||
<img src="https://i.imgur.com/I32VdMJ.png">
|
<img src="https://i.imgur.com/qwPNQbl.png">
|
||||||
|
<img src="https://i.imgur.com/s8yKQjx.png">
|
||||||
|
|
||||||
|
The benchmarks above were run on an Intel(R) Core(TM) i7-6820HQ CPU @ 2.70GHz
|
||||||
|
|
||||||
## [Guide](https://echo.labstack.com/guide)
|
## [Guide](https://echo.labstack.com/guide)
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
```go
|
```sh
|
||||||
// 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
|
||||||
```
|
```
|
||||||
@ -91,7 +94,7 @@ func hello(c echo.Context) error {
|
|||||||
|
|
||||||
## Help
|
## Help
|
||||||
|
|
||||||
- [Forum](https://forum.labstack.com)
|
- [Forum](https://github.com/labstack/echo/discussions)
|
||||||
- [Chat](https://gitter.im/labstack/echo)
|
- [Chat](https://gitter.im/labstack/echo)
|
||||||
|
|
||||||
## Contribute
|
## Contribute
|
||||||
|
61
vendor/github.com/labstack/echo/v4/bind.go
generated
vendored
61
vendor/github.com/labstack/echo/v4/bind.go
generated
vendored
@ -30,10 +30,8 @@ type (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Bind implements the `Binder#Bind` function.
|
// BindPathParams binds path params to bindable object
|
||||||
func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) {
|
func (b *DefaultBinder) BindPathParams(c Context, i interface{}) error {
|
||||||
req := c.Request()
|
|
||||||
|
|
||||||
names := c.ParamNames()
|
names := c.ParamNames()
|
||||||
values := c.ParamValues()
|
values := c.ParamValues()
|
||||||
params := map[string][]string{}
|
params := map[string][]string{}
|
||||||
@ -43,12 +41,28 @@ func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) {
|
|||||||
if err := b.bindData(i, params, "param"); err != nil {
|
if err := b.bindData(i, params, "param"); err != nil {
|
||||||
return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)
|
return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)
|
||||||
}
|
}
|
||||||
if err = b.bindData(i, c.QueryParams(), "query"); err != nil {
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindQueryParams binds query params to bindable object
|
||||||
|
func (b *DefaultBinder) BindQueryParams(c Context, i interface{}) error {
|
||||||
|
if err := b.bindData(i, c.QueryParams(), "query"); err != nil {
|
||||||
return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)
|
return NewHTTPError(http.StatusBadRequest, err.Error()).SetInternal(err)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindBody binds request body contents to bindable object
|
||||||
|
// NB: then binding forms take note that this implementation uses standard library form parsing
|
||||||
|
// which parses form data from BOTH URL and BODY if content type is not MIMEMultipartForm
|
||||||
|
// See non-MIMEMultipartForm: https://golang.org/pkg/net/http/#Request.ParseForm
|
||||||
|
// See MIMEMultipartForm: https://golang.org/pkg/net/http/#Request.ParseMultipartForm
|
||||||
|
func (b *DefaultBinder) BindBody(c Context, i interface{}) (err error) {
|
||||||
|
req := c.Request()
|
||||||
if req.ContentLength == 0 {
|
if req.ContentLength == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctype := req.Header.Get(HeaderContentType)
|
ctype := req.Header.Get(HeaderContentType)
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(ctype, MIMEApplicationJSON):
|
case strings.HasPrefix(ctype, MIMEApplicationJSON):
|
||||||
@ -80,15 +94,35 @@ func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) {
|
|||||||
default:
|
default:
|
||||||
return ErrUnsupportedMediaType
|
return ErrUnsupportedMediaType
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *DefaultBinder) bindData(ptr interface{}, data map[string][]string, tag string) error {
|
// Bind implements the `Binder#Bind` function.
|
||||||
if ptr == nil || len(data) == 0 {
|
// Binding is done in following order: 1) path params; 2) query params; 3) request body. Each step COULD override previous
|
||||||
|
// step binded values. For single source binding use their own methods BindBody, BindQueryParams, BindPathParams.
|
||||||
|
func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) {
|
||||||
|
if err := b.BindPathParams(c, i); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Issue #1670 - Query params are binded only for GET/DELETE and NOT for usual request with body (POST/PUT/PATCH)
|
||||||
|
// Reasoning here is that parameters in query and bind destination struct could have UNEXPECTED matches and results due that.
|
||||||
|
// i.e. is `&id=1&lang=en` from URL same as `{"id":100,"lang":"de"}` request body and which one should have priority when binding.
|
||||||
|
// This HTTP method check restores pre v4.1.11 behavior and avoids different problems when query is mixed with body
|
||||||
|
if c.Request().Method == http.MethodGet || c.Request().Method == http.MethodDelete {
|
||||||
|
if err = b.BindQueryParams(c, i); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b.BindBody(c, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// bindData will bind data ONLY fields in destination struct that have EXPLICIT tag
|
||||||
|
func (b *DefaultBinder) bindData(destination interface{}, data map[string][]string, tag string) error {
|
||||||
|
if destination == nil || len(data) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
typ := reflect.TypeOf(ptr).Elem()
|
typ := reflect.TypeOf(destination).Elem()
|
||||||
val := reflect.ValueOf(ptr).Elem()
|
val := reflect.ValueOf(destination).Elem()
|
||||||
|
|
||||||
// Map
|
// Map
|
||||||
if typ.Kind() == reflect.Map {
|
if typ.Kind() == reflect.Map {
|
||||||
@ -113,14 +147,15 @@ func (b *DefaultBinder) bindData(ptr interface{}, data map[string][]string, tag
|
|||||||
inputFieldName := typeField.Tag.Get(tag)
|
inputFieldName := typeField.Tag.Get(tag)
|
||||||
|
|
||||||
if inputFieldName == "" {
|
if inputFieldName == "" {
|
||||||
inputFieldName = typeField.Name
|
// 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 struct.
|
// structs that implement BindUnmarshaler are binded 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
|
||||||
}
|
}
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
// does not have explicit tag and is not an ordinary struct - so move to next field
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
inputValue, exists := data[inputFieldName]
|
inputValue, exists := data[inputFieldName]
|
||||||
|
1230
vendor/github.com/labstack/echo/v4/binder.go
generated
vendored
Normal file
1230
vendor/github.com/labstack/echo/v4/binder.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
11
vendor/github.com/labstack/echo/v4/codecov.yml
generated
vendored
Normal file
11
vendor/github.com/labstack/echo/v4/codecov.yml
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
coverage:
|
||||||
|
status:
|
||||||
|
project:
|
||||||
|
default:
|
||||||
|
threshold: 1%
|
||||||
|
patch:
|
||||||
|
default:
|
||||||
|
threshold: 1%
|
||||||
|
|
||||||
|
comment:
|
||||||
|
require_changes: true
|
34
vendor/github.com/labstack/echo/v4/context.go
generated
vendored
34
vendor/github.com/labstack/echo/v4/context.go
generated
vendored
@ -246,7 +246,7 @@ func (c *context) IsTLS() bool {
|
|||||||
|
|
||||||
func (c *context) IsWebSocket() bool {
|
func (c *context) IsWebSocket() bool {
|
||||||
upgrade := c.request.Header.Get(HeaderUpgrade)
|
upgrade := c.request.Header.Get(HeaderUpgrade)
|
||||||
return strings.ToLower(upgrade) == "websocket"
|
return strings.EqualFold(upgrade, "websocket")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *context) Scheme() string {
|
func (c *context) Scheme() string {
|
||||||
@ -276,7 +276,11 @@ func (c *context) RealIP() string {
|
|||||||
}
|
}
|
||||||
// Fall back to legacy behavior
|
// Fall back to legacy behavior
|
||||||
if ip := c.request.Header.Get(HeaderXForwardedFor); ip != "" {
|
if ip := c.request.Header.Get(HeaderXForwardedFor); ip != "" {
|
||||||
return strings.Split(ip, ", ")[0]
|
i := strings.IndexAny(ip, ", ")
|
||||||
|
if i > 0 {
|
||||||
|
return ip[:i]
|
||||||
|
}
|
||||||
|
return ip
|
||||||
}
|
}
|
||||||
if ip := c.request.Header.Get(HeaderXRealIP); ip != "" {
|
if ip := c.request.Header.Get(HeaderXRealIP); ip != "" {
|
||||||
return ip
|
return ip
|
||||||
@ -310,7 +314,19 @@ func (c *context) ParamNames() []string {
|
|||||||
|
|
||||||
func (c *context) SetParamNames(names ...string) {
|
func (c *context) SetParamNames(names ...string) {
|
||||||
c.pnames = names
|
c.pnames = names
|
||||||
*c.echo.maxParam = len(names)
|
|
||||||
|
l := len(names)
|
||||||
|
if *c.echo.maxParam < l {
|
||||||
|
*c.echo.maxParam = 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,
|
||||||
|
// probably those values will be overriden in a Context#SetParamValues
|
||||||
|
newPvalues := make([]string, l)
|
||||||
|
copy(newPvalues, c.pvalues)
|
||||||
|
c.pvalues = newPvalues
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *context) ParamValues() []string {
|
func (c *context) ParamValues() []string {
|
||||||
@ -318,7 +334,15 @@ func (c *context) ParamValues() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *context) SetParamValues(values ...string) {
|
func (c *context) SetParamValues(values ...string) {
|
||||||
c.pvalues = values
|
// NOTE: Don't just set c.pvalues = values, because it has to have length c.echo.maxParam at all times
|
||||||
|
// It will brake the Router#Find code
|
||||||
|
limit := len(values)
|
||||||
|
if limit > *c.echo.maxParam {
|
||||||
|
limit = *c.echo.maxParam
|
||||||
|
}
|
||||||
|
for i := 0; i < limit; i++ {
|
||||||
|
c.pvalues[i] = values[i]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *context) QueryParam(name string) string {
|
func (c *context) QueryParam(name string) string {
|
||||||
@ -361,7 +385,7 @@ func (c *context) FormFile(name string) (*multipart.FileHeader, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
f.Close()
|
||||||
return fh, nil
|
return fh, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
167
vendor/github.com/labstack/echo/v4/echo.go
generated
vendored
167
vendor/github.com/labstack/echo/v4/echo.go
generated
vendored
@ -48,7 +48,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -67,6 +67,9 @@ type (
|
|||||||
// Echo is the top-level framework instance.
|
// Echo is the top-level framework instance.
|
||||||
Echo struct {
|
Echo struct {
|
||||||
common
|
common
|
||||||
|
// startupMutex is mutex to lock Echo instance access during server configuration and startup. Useful for to get
|
||||||
|
// listener address info (on which interface/port was listener binded) without having data races.
|
||||||
|
startupMutex sync.RWMutex
|
||||||
StdLogger *stdLog.Logger
|
StdLogger *stdLog.Logger
|
||||||
colorer *color.Color
|
colorer *color.Color
|
||||||
premiddleware []MiddlewareFunc
|
premiddleware []MiddlewareFunc
|
||||||
@ -91,6 +94,7 @@ type (
|
|||||||
Renderer Renderer
|
Renderer Renderer
|
||||||
Logger Logger
|
Logger Logger
|
||||||
IPExtractor IPExtractor
|
IPExtractor IPExtractor
|
||||||
|
ListenerNetwork string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Route contains a handler and information for matching against requests.
|
// Route contains a handler and information for matching against requests.
|
||||||
@ -230,7 +234,7 @@ const (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
// Version of Echo
|
// Version of Echo
|
||||||
Version = "4.1.16"
|
Version = "4.2.1"
|
||||||
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 = `
|
||||||
@ -280,6 +284,7 @@ var (
|
|||||||
ErrInvalidRedirectCode = errors.New("invalid redirect status code")
|
ErrInvalidRedirectCode = errors.New("invalid redirect status code")
|
||||||
ErrCookieNotFound = errors.New("cookie not found")
|
ErrCookieNotFound = errors.New("cookie not found")
|
||||||
ErrInvalidCertOrKeyType = errors.New("invalid cert or key type, must be string or []byte")
|
ErrInvalidCertOrKeyType = errors.New("invalid cert or key type, must be string or []byte")
|
||||||
|
ErrInvalidListenerNetwork = errors.New("invalid listener network")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Error handlers
|
// Error handlers
|
||||||
@ -301,9 +306,10 @@ func New() (e *Echo) {
|
|||||||
AutoTLSManager: autocert.Manager{
|
AutoTLSManager: autocert.Manager{
|
||||||
Prompt: autocert.AcceptTOS,
|
Prompt: autocert.AcceptTOS,
|
||||||
},
|
},
|
||||||
Logger: log.New("echo"),
|
Logger: log.New("echo"),
|
||||||
colorer: color.New(),
|
colorer: color.New(),
|
||||||
maxParam: new(int),
|
maxParam: new(int),
|
||||||
|
ListenerNetwork: "tcp",
|
||||||
}
|
}
|
||||||
e.Server.Handler = e
|
e.Server.Handler = e
|
||||||
e.TLSServer.Handler = e
|
e.TLSServer.Handler = e
|
||||||
@ -361,10 +367,12 @@ 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 e.Debug {
|
if m, ok := he.Message.(string); ok {
|
||||||
message = err.Error()
|
if e.Debug {
|
||||||
} else if m, ok := message.(string); ok {
|
message = Map{"message": m, "error": err.Error()}
|
||||||
message = Map{"message": m}
|
} else {
|
||||||
|
message = Map{"message": m}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send response
|
// Send response
|
||||||
@ -479,11 +487,31 @@ func (common) static(prefix, root string, get func(string, HandlerFunc, ...Middl
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
name := filepath.Join(root, path.Clean("/"+p)) // "/"+ for security
|
|
||||||
|
name := filepath.Join(root, filepath.Clean("/"+p)) // "/"+ for security
|
||||||
|
fi, err := os.Stat(name)
|
||||||
|
if err != nil {
|
||||||
|
// The access path does not exist
|
||||||
|
return NotFoundHandler(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the request is for a directory and does not end with "/"
|
||||||
|
p = c.Request().URL.Path // path must not be empty.
|
||||||
|
if fi.IsDir() && p[len(p)-1] != '/' {
|
||||||
|
// Redirect to ends with "/"
|
||||||
|
return c.Redirect(http.StatusMovedPermanently, p+"/")
|
||||||
|
}
|
||||||
return c.File(name)
|
return c.File(name)
|
||||||
}
|
}
|
||||||
if prefix == "/" {
|
// Handle added routes based on trailing slash:
|
||||||
return get(prefix+"*", h)
|
// /prefix => exact route "/prefix" + any route "/prefix/*"
|
||||||
|
// /prefix/ => only any route "/prefix/*"
|
||||||
|
if prefix != "" {
|
||||||
|
if prefix[len(prefix)-1] == '/' {
|
||||||
|
// Only add any route for intentional trailing slash
|
||||||
|
return get(prefix+"*", h)
|
||||||
|
}
|
||||||
|
get(prefix, h)
|
||||||
}
|
}
|
||||||
return get(prefix+"/*", h)
|
return get(prefix+"/*", h)
|
||||||
}
|
}
|
||||||
@ -504,11 +532,7 @@ func (e *Echo) add(host, method, path string, handler HandlerFunc, middleware ..
|
|||||||
name := handlerName(handler)
|
name := handlerName(handler)
|
||||||
router := e.findRouter(host)
|
router := e.findRouter(host)
|
||||||
router.Add(method, path, func(c Context) error {
|
router.Add(method, path, func(c Context) error {
|
||||||
h := handler
|
h := applyMiddleware(handler, middleware...)
|
||||||
// Chain middleware
|
|
||||||
for i := len(middleware) - 1; i >= 0; i-- {
|
|
||||||
h = middleware[i](h)
|
|
||||||
}
|
|
||||||
return h(c)
|
return h(c)
|
||||||
})
|
})
|
||||||
r := &Route{
|
r := &Route{
|
||||||
@ -560,7 +584,7 @@ func (e *Echo) Reverse(name string, params ...interface{}) string {
|
|||||||
for _, r := range e.router.routes {
|
for _, r := range e.router.routes {
|
||||||
if r.Name == name {
|
if r.Name == name {
|
||||||
for i, l := 0, len(r.Path); i < l; i++ {
|
for i, l := 0, len(r.Path); i < l; i++ {
|
||||||
if r.Path[i] == ':' && n < ln {
|
if (r.Path[i] == ':' || r.Path[i] == '*') && n < ln {
|
||||||
for ; i < l && r.Path[i] != '/'; i++ {
|
for ; i < l && r.Path[i] != '/'; i++ {
|
||||||
}
|
}
|
||||||
uri.WriteString(fmt.Sprintf("%v", params[n]))
|
uri.WriteString(fmt.Sprintf("%v", params[n]))
|
||||||
@ -602,16 +626,15 @@ func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
// Acquire context
|
// Acquire context
|
||||||
c := e.pool.Get().(*context)
|
c := e.pool.Get().(*context)
|
||||||
c.Reset(r, w)
|
c.Reset(r, w)
|
||||||
|
|
||||||
h := NotFoundHandler
|
h := NotFoundHandler
|
||||||
|
|
||||||
if e.premiddleware == nil {
|
if e.premiddleware == nil {
|
||||||
e.findRouter(r.Host).Find(r.Method, getPath(r), c)
|
e.findRouter(r.Host).Find(r.Method, GetPath(r), c)
|
||||||
h = c.Handler()
|
h = c.Handler()
|
||||||
h = applyMiddleware(h, e.middleware...)
|
h = applyMiddleware(h, e.middleware...)
|
||||||
} else {
|
} else {
|
||||||
h = func(c Context) error {
|
h = func(c Context) error {
|
||||||
e.findRouter(r.Host).Find(r.Method, getPath(r), c)
|
e.findRouter(r.Host).Find(r.Method, GetPath(r), c)
|
||||||
h := c.Handler()
|
h := c.Handler()
|
||||||
h = applyMiddleware(h, e.middleware...)
|
h = applyMiddleware(h, e.middleware...)
|
||||||
return h(c)
|
return h(c)
|
||||||
@ -630,21 +653,30 @@ func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// Start starts an HTTP server.
|
// Start starts an HTTP server.
|
||||||
func (e *Echo) Start(address string) error {
|
func (e *Echo) Start(address string) error {
|
||||||
|
e.startupMutex.Lock()
|
||||||
e.Server.Addr = address
|
e.Server.Addr = address
|
||||||
return e.StartServer(e.Server)
|
if err := e.configureServer(e.Server); err != nil {
|
||||||
|
e.startupMutex.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
e.startupMutex.Unlock()
|
||||||
|
return e.Server.Serve(e.Listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartTLS starts an HTTPS server.
|
// StartTLS starts an HTTPS server.
|
||||||
// If `certFile` or `keyFile` is `string` the values are treated as file paths.
|
// If `certFile` or `keyFile` is `string` the values are treated as file paths.
|
||||||
// If `certFile` or `keyFile` is `[]byte` the values are treated as the certificate or key as-is.
|
// If `certFile` or `keyFile` is `[]byte` the values are treated as the certificate or key as-is.
|
||||||
func (e *Echo) StartTLS(address string, certFile, keyFile interface{}) (err error) {
|
func (e *Echo) StartTLS(address string, certFile, keyFile interface{}) (err error) {
|
||||||
|
e.startupMutex.Lock()
|
||||||
var cert []byte
|
var cert []byte
|
||||||
if cert, err = filepathOrContent(certFile); err != nil {
|
if cert, err = filepathOrContent(certFile); err != nil {
|
||||||
|
e.startupMutex.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var key []byte
|
var key []byte
|
||||||
if key, err = filepathOrContent(keyFile); err != nil {
|
if key, err = filepathOrContent(keyFile); err != nil {
|
||||||
|
e.startupMutex.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -652,10 +684,17 @@ func (e *Echo) StartTLS(address string, certFile, keyFile interface{}) (err erro
|
|||||||
s.TLSConfig = new(tls.Config)
|
s.TLSConfig = new(tls.Config)
|
||||||
s.TLSConfig.Certificates = make([]tls.Certificate, 1)
|
s.TLSConfig.Certificates = make([]tls.Certificate, 1)
|
||||||
if s.TLSConfig.Certificates[0], err = tls.X509KeyPair(cert, key); err != nil {
|
if s.TLSConfig.Certificates[0], err = tls.X509KeyPair(cert, key); err != nil {
|
||||||
|
e.startupMutex.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return e.startTLS(address)
|
e.configureTLS(address)
|
||||||
|
if err := e.configureServer(s); err != nil {
|
||||||
|
e.startupMutex.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
e.startupMutex.Unlock()
|
||||||
|
return s.Serve(e.TLSListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
func filepathOrContent(fileOrContent interface{}) (content []byte, err error) {
|
func filepathOrContent(fileOrContent interface{}) (content []byte, err error) {
|
||||||
@ -671,24 +710,45 @@ func filepathOrContent(fileOrContent interface{}) (content []byte, err error) {
|
|||||||
|
|
||||||
// StartAutoTLS starts an HTTPS server using certificates automatically installed from https://letsencrypt.org.
|
// StartAutoTLS starts an HTTPS server using certificates automatically installed from https://letsencrypt.org.
|
||||||
func (e *Echo) StartAutoTLS(address string) error {
|
func (e *Echo) StartAutoTLS(address string) error {
|
||||||
|
e.startupMutex.Lock()
|
||||||
s := e.TLSServer
|
s := e.TLSServer
|
||||||
s.TLSConfig = new(tls.Config)
|
s.TLSConfig = new(tls.Config)
|
||||||
s.TLSConfig.GetCertificate = e.AutoTLSManager.GetCertificate
|
s.TLSConfig.GetCertificate = e.AutoTLSManager.GetCertificate
|
||||||
s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, acme.ALPNProto)
|
s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, acme.ALPNProto)
|
||||||
return e.startTLS(address)
|
|
||||||
|
e.configureTLS(address)
|
||||||
|
if err := e.configureServer(s); err != nil {
|
||||||
|
e.startupMutex.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
e.startupMutex.Unlock()
|
||||||
|
return s.Serve(e.TLSListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Echo) startTLS(address string) error {
|
func (e *Echo) configureTLS(address string) {
|
||||||
s := e.TLSServer
|
s := e.TLSServer
|
||||||
s.Addr = address
|
s.Addr = address
|
||||||
if !e.DisableHTTP2 {
|
if !e.DisableHTTP2 {
|
||||||
s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, "h2")
|
s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, "h2")
|
||||||
}
|
}
|
||||||
return e.StartServer(e.TLSServer)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartServer starts a custom http server.
|
// StartServer starts a custom http server.
|
||||||
func (e *Echo) StartServer(s *http.Server) (err error) {
|
func (e *Echo) StartServer(s *http.Server) (err error) {
|
||||||
|
e.startupMutex.Lock()
|
||||||
|
if err := e.configureServer(s); err != nil {
|
||||||
|
e.startupMutex.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if s.TLSConfig != nil {
|
||||||
|
e.startupMutex.Unlock()
|
||||||
|
return s.Serve(e.TLSListener)
|
||||||
|
}
|
||||||
|
e.startupMutex.Unlock()
|
||||||
|
return s.Serve(e.Listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Echo) configureServer(s *http.Server) (err error) {
|
||||||
// Setup
|
// Setup
|
||||||
e.colorer.SetOutput(e.Logger.Output())
|
e.colorer.SetOutput(e.Logger.Output())
|
||||||
s.ErrorLog = e.StdLogger
|
s.ErrorLog = e.StdLogger
|
||||||
@ -703,7 +763,7 @@ func (e *Echo) StartServer(s *http.Server) (err error) {
|
|||||||
|
|
||||||
if s.TLSConfig == nil {
|
if s.TLSConfig == nil {
|
||||||
if e.Listener == nil {
|
if e.Listener == nil {
|
||||||
e.Listener, err = newListener(s.Addr)
|
e.Listener, err = newListener(s.Addr, e.ListenerNetwork)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -711,10 +771,10 @@ func (e *Echo) StartServer(s *http.Server) (err error) {
|
|||||||
if !e.HidePort {
|
if !e.HidePort {
|
||||||
e.colorer.Printf("⇨ http server started on %s\n", e.colorer.Green(e.Listener.Addr()))
|
e.colorer.Printf("⇨ http server started on %s\n", e.colorer.Green(e.Listener.Addr()))
|
||||||
}
|
}
|
||||||
return s.Serve(e.Listener)
|
return nil
|
||||||
}
|
}
|
||||||
if e.TLSListener == nil {
|
if e.TLSListener == nil {
|
||||||
l, err := newListener(s.Addr)
|
l, err := newListener(s.Addr, e.ListenerNetwork)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -723,11 +783,32 @@ func (e *Echo) StartServer(s *http.Server) (err error) {
|
|||||||
if !e.HidePort {
|
if !e.HidePort {
|
||||||
e.colorer.Printf("⇨ https server started on %s\n", e.colorer.Green(e.TLSListener.Addr()))
|
e.colorer.Printf("⇨ https server started on %s\n", e.colorer.Green(e.TLSListener.Addr()))
|
||||||
}
|
}
|
||||||
return s.Serve(e.TLSListener)
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenerAddr returns net.Addr for Listener
|
||||||
|
func (e *Echo) ListenerAddr() net.Addr {
|
||||||
|
e.startupMutex.RLock()
|
||||||
|
defer e.startupMutex.RUnlock()
|
||||||
|
if e.Listener == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return e.Listener.Addr()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLSListenerAddr returns net.Addr for TLSListener
|
||||||
|
func (e *Echo) TLSListenerAddr() net.Addr {
|
||||||
|
e.startupMutex.RLock()
|
||||||
|
defer e.startupMutex.RUnlock()
|
||||||
|
if e.TLSListener == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return e.TLSListener.Addr()
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartH2CServer starts a custom http/2 server with h2c (HTTP/2 Cleartext).
|
// StartH2CServer starts a custom http/2 server with h2c (HTTP/2 Cleartext).
|
||||||
func (e *Echo) StartH2CServer(address string, h2s *http2.Server) (err error) {
|
func (e *Echo) StartH2CServer(address string, h2s *http2.Server) (err error) {
|
||||||
|
e.startupMutex.Lock()
|
||||||
// Setup
|
// Setup
|
||||||
s := e.Server
|
s := e.Server
|
||||||
s.Addr = address
|
s.Addr = address
|
||||||
@ -743,20 +824,24 @@ func (e *Echo) StartH2CServer(address string, h2s *http2.Server) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if e.Listener == nil {
|
if e.Listener == nil {
|
||||||
e.Listener, err = newListener(s.Addr)
|
e.Listener, err = newListener(s.Addr, e.ListenerNetwork)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
e.startupMutex.Unlock()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !e.HidePort {
|
if !e.HidePort {
|
||||||
e.colorer.Printf("⇨ http server started on %s\n", e.colorer.Green(e.Listener.Addr()))
|
e.colorer.Printf("⇨ http server started on %s\n", e.colorer.Green(e.Listener.Addr()))
|
||||||
}
|
}
|
||||||
|
e.startupMutex.Unlock()
|
||||||
return s.Serve(e.Listener)
|
return s.Serve(e.Listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close immediately stops the server.
|
// Close immediately stops the server.
|
||||||
// It internally calls `http.Server#Close()`.
|
// It internally calls `http.Server#Close()`.
|
||||||
func (e *Echo) Close() error {
|
func (e *Echo) Close() error {
|
||||||
|
e.startupMutex.Lock()
|
||||||
|
defer e.startupMutex.Unlock()
|
||||||
if err := e.TLSServer.Close(); err != nil {
|
if err := e.TLSServer.Close(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -766,6 +851,8 @@ func (e *Echo) Close() error {
|
|||||||
// Shutdown stops the server gracefully.
|
// Shutdown stops the server gracefully.
|
||||||
// It internally calls `http.Server#Shutdown()`.
|
// It internally calls `http.Server#Shutdown()`.
|
||||||
func (e *Echo) Shutdown(ctx stdContext.Context) error {
|
func (e *Echo) Shutdown(ctx stdContext.Context) error {
|
||||||
|
e.startupMutex.Lock()
|
||||||
|
defer e.startupMutex.Unlock()
|
||||||
if err := e.TLSServer.Shutdown(ctx); err != nil {
|
if err := e.TLSServer.Shutdown(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -795,6 +882,11 @@ func (he *HTTPError) SetInternal(err error) *HTTPError {
|
|||||||
return he
|
return he
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unwrap satisfies the Go 1.13 error wrapper interface.
|
||||||
|
func (he *HTTPError) Unwrap() error {
|
||||||
|
return he.Internal
|
||||||
|
}
|
||||||
|
|
||||||
// WrapHandler wraps `http.Handler` into `echo.HandlerFunc`.
|
// WrapHandler wraps `http.Handler` into `echo.HandlerFunc`.
|
||||||
func WrapHandler(h http.Handler) HandlerFunc {
|
func WrapHandler(h http.Handler) HandlerFunc {
|
||||||
return func(c Context) error {
|
return func(c Context) error {
|
||||||
@ -817,7 +909,11 @@ func WrapMiddleware(m func(http.Handler) http.Handler) MiddlewareFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPath(r *http.Request) string {
|
// GetPath returns RawPath, if it's empty returns Path from URL
|
||||||
|
// Difference between RawPath and Path is:
|
||||||
|
// * Path is where request path is stored. Value is stored in decoded form: /%47%6f%2f becomes /Go/.
|
||||||
|
// * RawPath is an optional field which only gets set if the default encoding is different from Path.
|
||||||
|
func GetPath(r *http.Request) string {
|
||||||
path := r.URL.RawPath
|
path := r.URL.RawPath
|
||||||
if path == "" {
|
if path == "" {
|
||||||
path = r.URL.Path
|
path = r.URL.Path
|
||||||
@ -867,8 +963,11 @@ func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func newListener(address string) (*tcpKeepAliveListener, error) {
|
func newListener(address, network string) (*tcpKeepAliveListener, error) {
|
||||||
l, err := net.Listen("tcp", address)
|
if network != "tcp" && network != "tcp4" && network != "tcp6" {
|
||||||
|
return nil, ErrInvalidListenerNetwork
|
||||||
|
}
|
||||||
|
l, err := net.Listen(network, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
14
vendor/github.com/labstack/echo/v4/go.mod
generated
vendored
14
vendor/github.com/labstack/echo/v4/go.mod
generated
vendored
@ -1,14 +1,16 @@
|
|||||||
module github.com/labstack/echo/v4
|
module github.com/labstack/echo/v4
|
||||||
|
|
||||||
go 1.14
|
go 1.15
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||||
github.com/labstack/gommon v0.3.0
|
github.com/labstack/gommon v0.3.0
|
||||||
github.com/mattn/go-colorable v0.1.6 // indirect
|
github.com/mattn/go-colorable v0.1.7 // indirect
|
||||||
github.com/stretchr/testify v1.4.0
|
github.com/stretchr/testify v1.4.0
|
||||||
github.com/valyala/fasttemplate v1.1.0
|
github.com/valyala/fasttemplate v1.2.1
|
||||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d
|
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b
|
golang.org/x/net v0.0.0-20200822124328-c89045814202
|
||||||
golang.org/x/text v0.3.2 // indirect
|
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6 // indirect
|
||||||
|
golang.org/x/text v0.3.3 // indirect
|
||||||
|
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324
|
||||||
)
|
)
|
||||||
|
28
vendor/github.com/labstack/echo/v4/go.sum
generated
vendored
28
vendor/github.com/labstack/echo/v4/go.sum
generated
vendored
@ -1,14 +1,13 @@
|
|||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dgrijalva/jwt-go v1.0.2 h1:KPldsxuKGsS2FPWsNeg9ZO18aCrGKujPoWXn2yo+KQM=
|
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
|
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
|
||||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
||||||
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
||||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
|
github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw=
|
||||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg=
|
github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg=
|
||||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||||
@ -23,14 +22,15 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw
|
|||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8=
|
github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8=
|
||||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||||
github.com/valyala/fasttemplate v1.1.0 h1:RZqt0yGBsps8NGvLSGW804QQqCUYYLsaOjTVHy1Ocw4=
|
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
|
||||||
github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAakHVhecoE5wlSg5GjnafJGw=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
|
||||||
|
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -39,11 +39,17 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6 h1:DvY3Zkh7KabQE/kfzMvYvKirSiguP9Q/veMtkYyf0o8=
|
||||||
|
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE=
|
||||||
|
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
2
vendor/github.com/labstack/echo/v4/group.go
generated
vendored
2
vendor/github.com/labstack/echo/v4/group.go
generated
vendored
@ -109,7 +109,7 @@ func (g *Group) Static(prefix, root string) {
|
|||||||
|
|
||||||
// File implements `Echo#File()` for sub-routes within the Group.
|
// File implements `Echo#File()` for sub-routes within the Group.
|
||||||
func (g *Group) File(path, file string) {
|
func (g *Group) File(path, file string) {
|
||||||
g.file(g.prefix+path, file, g.GET)
|
g.file(path, file, g.GET)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add implements `Echo#Add()` for sub-routes within the Group.
|
// Add implements `Echo#Add()` for sub-routes within the Group.
|
||||||
|
2
vendor/github.com/labstack/echo/v4/middleware/basic_auth.go
generated
vendored
2
vendor/github.com/labstack/echo/v4/middleware/basic_auth.go
generated
vendored
@ -73,7 +73,7 @@ func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc {
|
|||||||
auth := c.Request().Header.Get(echo.HeaderAuthorization)
|
auth := c.Request().Header.Get(echo.HeaderAuthorization)
|
||||||
l := len(basic)
|
l := len(basic)
|
||||||
|
|
||||||
if len(auth) > l+1 && strings.ToLower(auth[:l]) == basic {
|
if len(auth) > l+1 && strings.EqualFold(auth[:l], basic) {
|
||||||
b, err := base64.StdEncoding.DecodeString(auth[l+1:])
|
b, err := base64.StdEncoding.DecodeString(auth[l+1:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
33
vendor/github.com/labstack/echo/v4/middleware/compress.go
generated
vendored
33
vendor/github.com/labstack/echo/v4/middleware/compress.go
generated
vendored
@ -8,6 +8,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
@ -58,6 +59,8 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
|
|||||||
config.Level = DefaultGzipConfig.Level
|
config.Level = DefaultGzipConfig.Level
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pool := gzipCompressPool(config)
|
||||||
|
|
||||||
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 {
|
||||||
if config.Skipper(c) {
|
if config.Skipper(c) {
|
||||||
@ -68,11 +71,13 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
|
|||||||
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
|
res.Header().Set(echo.HeaderContentEncoding, gzipScheme) // Issue #806
|
||||||
rw := res.Writer
|
i := pool.Get()
|
||||||
w, err := gzip.NewWriterLevel(rw, config.Level)
|
w, ok := i.(*gzip.Writer)
|
||||||
if err != nil {
|
if !ok {
|
||||||
return err
|
return echo.NewHTTPError(http.StatusInternalServerError, i.(error).Error())
|
||||||
}
|
}
|
||||||
|
rw := res.Writer
|
||||||
|
w.Reset(rw)
|
||||||
defer func() {
|
defer func() {
|
||||||
if res.Size == 0 {
|
if res.Size == 0 {
|
||||||
if res.Header().Get(echo.HeaderContentEncoding) == gzipScheme {
|
if res.Header().Get(echo.HeaderContentEncoding) == gzipScheme {
|
||||||
@ -85,6 +90,7 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
|
|||||||
w.Reset(ioutil.Discard)
|
w.Reset(ioutil.Discard)
|
||||||
}
|
}
|
||||||
w.Close()
|
w.Close()
|
||||||
|
pool.Put(w)
|
||||||
}()
|
}()
|
||||||
grw := &gzipResponseWriter{Writer: w, ResponseWriter: rw}
|
grw := &gzipResponseWriter{Writer: w, ResponseWriter: rw}
|
||||||
res.Writer = grw
|
res.Writer = grw
|
||||||
@ -119,3 +125,22 @@ func (w *gzipResponseWriter) Flush() {
|
|||||||
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 w.ResponseWriter.(http.Hijacker).Hijack()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *gzipResponseWriter) Push(target string, opts *http.PushOptions) error {
|
||||||
|
if p, ok := w.ResponseWriter.(http.Pusher); ok {
|
||||||
|
return p.Push(target, opts)
|
||||||
|
}
|
||||||
|
return http.ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
func gzipCompressPool(config GzipConfig) sync.Pool {
|
||||||
|
return sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
w, err := gzip.NewWriterLevel(ioutil.Discard, config.Level)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return w
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
90
vendor/github.com/labstack/echo/v4/middleware/cors.go
generated
vendored
90
vendor/github.com/labstack/echo/v4/middleware/cors.go
generated
vendored
@ -2,6 +2,7 @@ package middleware
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -18,6 +19,13 @@ type (
|
|||||||
// Optional. Default value []string{"*"}.
|
// Optional. Default value []string{"*"}.
|
||||||
AllowOrigins []string `yaml:"allow_origins"`
|
AllowOrigins []string `yaml:"allow_origins"`
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// an error is returned, it is returned by the handler. If this option is
|
||||||
|
// set, AllowOrigins is ignored.
|
||||||
|
// Optional.
|
||||||
|
AllowOriginFunc func(origin string) (bool, error) `yaml:"allow_origin_func"`
|
||||||
|
|
||||||
// AllowMethods defines a list methods allowed when accessing the resource.
|
// AllowMethods defines a list methods allowed when accessing the resource.
|
||||||
// This is used in response to a preflight request.
|
// This is used in response to a preflight request.
|
||||||
// Optional. Default value DefaultCORSConfig.AllowMethods.
|
// Optional. Default value DefaultCORSConfig.AllowMethods.
|
||||||
@ -76,6 +84,15 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
|
|||||||
config.AllowMethods = DefaultCORSConfig.AllowMethods
|
config.AllowMethods = DefaultCORSConfig.AllowMethods
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allowOriginPatterns := []string{}
|
||||||
|
for _, origin := range config.AllowOrigins {
|
||||||
|
pattern := regexp.QuoteMeta(origin)
|
||||||
|
pattern = strings.Replace(pattern, "\\*", ".*", -1)
|
||||||
|
pattern = strings.Replace(pattern, "\\?", ".", -1)
|
||||||
|
pattern = "^" + pattern + "$"
|
||||||
|
allowOriginPatterns = append(allowOriginPatterns, pattern)
|
||||||
|
}
|
||||||
|
|
||||||
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, ",")
|
||||||
@ -92,25 +109,73 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
|
|||||||
origin := req.Header.Get(echo.HeaderOrigin)
|
origin := req.Header.Get(echo.HeaderOrigin)
|
||||||
allowOrigin := ""
|
allowOrigin := ""
|
||||||
|
|
||||||
// Check allowed origins
|
preflight := req.Method == http.MethodOptions
|
||||||
for _, o := range config.AllowOrigins {
|
res.Header().Add(echo.HeaderVary, echo.HeaderOrigin)
|
||||||
if o == "*" && config.AllowCredentials {
|
|
||||||
allowOrigin = origin
|
// No Origin provided
|
||||||
break
|
if origin == "" {
|
||||||
|
if !preflight {
|
||||||
|
return next(c)
|
||||||
}
|
}
|
||||||
if o == "*" || o == origin {
|
return c.NoContent(http.StatusNoContent)
|
||||||
allowOrigin = o
|
}
|
||||||
break
|
|
||||||
|
if config.AllowOriginFunc != nil {
|
||||||
|
allowed, err := config.AllowOriginFunc(origin)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
if matchSubdomain(origin, o) {
|
if allowed {
|
||||||
allowOrigin = origin
|
allowOrigin = origin
|
||||||
break
|
}
|
||||||
|
} else {
|
||||||
|
// Check allowed origins
|
||||||
|
for _, o := range config.AllowOrigins {
|
||||||
|
if o == "*" && config.AllowCredentials {
|
||||||
|
allowOrigin = origin
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if o == "*" || o == origin {
|
||||||
|
allowOrigin = o
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if matchSubdomain(origin, o) {
|
||||||
|
allowOrigin = origin
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check allowed origin patterns
|
||||||
|
for _, re := range allowOriginPatterns {
|
||||||
|
if allowOrigin == "" {
|
||||||
|
didx := strings.Index(origin, "://")
|
||||||
|
if didx == -1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
domAuth := origin[didx+3:]
|
||||||
|
// to avoid regex cost by invalid long domain
|
||||||
|
if len(domAuth) > 253 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if match, _ := regexp.MatchString(re, origin); match {
|
||||||
|
allowOrigin = origin
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Origin not allowed
|
||||||
|
if allowOrigin == "" {
|
||||||
|
if !preflight {
|
||||||
|
return next(c)
|
||||||
|
}
|
||||||
|
return c.NoContent(http.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
// Simple request
|
// Simple request
|
||||||
if req.Method != http.MethodOptions {
|
if !preflight {
|
||||||
res.Header().Add(echo.HeaderVary, echo.HeaderOrigin)
|
|
||||||
res.Header().Set(echo.HeaderAccessControlAllowOrigin, allowOrigin)
|
res.Header().Set(echo.HeaderAccessControlAllowOrigin, allowOrigin)
|
||||||
if config.AllowCredentials {
|
if config.AllowCredentials {
|
||||||
res.Header().Set(echo.HeaderAccessControlAllowCredentials, "true")
|
res.Header().Set(echo.HeaderAccessControlAllowCredentials, "true")
|
||||||
@ -122,7 +187,6 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Preflight request
|
// Preflight request
|
||||||
res.Header().Add(echo.HeaderVary, echo.HeaderOrigin)
|
|
||||||
res.Header().Add(echo.HeaderVary, echo.HeaderAccessControlRequestMethod)
|
res.Header().Add(echo.HeaderVary, echo.HeaderAccessControlRequestMethod)
|
||||||
res.Header().Add(echo.HeaderVary, echo.HeaderAccessControlRequestHeaders)
|
res.Header().Add(echo.HeaderVary, echo.HeaderAccessControlRequestHeaders)
|
||||||
res.Header().Set(echo.HeaderAccessControlAllowOrigin, allowOrigin)
|
res.Header().Set(echo.HeaderAccessControlAllowOrigin, allowOrigin)
|
||||||
|
23
vendor/github.com/labstack/echo/v4/middleware/csrf.go
generated
vendored
23
vendor/github.com/labstack/echo/v4/middleware/csrf.go
generated
vendored
@ -57,6 +57,10 @@ type (
|
|||||||
// 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.
|
||||||
|
// Optional. Default value SameSiteDefaultMode.
|
||||||
|
CookieSameSite http.SameSite `yaml:"cookie_same_site"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// csrfTokenExtractor defines a function that takes `echo.Context` and returns
|
// csrfTokenExtractor defines a function that takes `echo.Context` and returns
|
||||||
@ -67,12 +71,13 @@ type (
|
|||||||
var (
|
var (
|
||||||
// DefaultCSRFConfig is the default CSRF middleware config.
|
// DefaultCSRFConfig is the default CSRF middleware config.
|
||||||
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,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -105,6 +110,9 @@ func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc {
|
|||||||
if config.CookieMaxAge == 0 {
|
if config.CookieMaxAge == 0 {
|
||||||
config.CookieMaxAge = DefaultCSRFConfig.CookieMaxAge
|
config.CookieMaxAge = DefaultCSRFConfig.CookieMaxAge
|
||||||
}
|
}
|
||||||
|
if config.CookieSameSite == SameSiteNoneMode {
|
||||||
|
config.CookieSecure = true
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize
|
// Initialize
|
||||||
parts := strings.Split(config.TokenLookup, ":")
|
parts := strings.Split(config.TokenLookup, ":")
|
||||||
@ -157,6 +165,9 @@ func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc {
|
|||||||
if config.CookieDomain != "" {
|
if config.CookieDomain != "" {
|
||||||
cookie.Domain = config.CookieDomain
|
cookie.Domain = config.CookieDomain
|
||||||
}
|
}
|
||||||
|
if config.CookieSameSite != http.SameSiteDefaultMode {
|
||||||
|
cookie.SameSite = config.CookieSameSite
|
||||||
|
}
|
||||||
cookie.Expires = time.Now().Add(time.Duration(config.CookieMaxAge) * time.Second)
|
cookie.Expires = time.Now().Add(time.Duration(config.CookieMaxAge) * time.Second)
|
||||||
cookie.Secure = config.CookieSecure
|
cookie.Secure = config.CookieSecure
|
||||||
cookie.HttpOnly = config.CookieHTTPOnly
|
cookie.HttpOnly = config.CookieHTTPOnly
|
||||||
|
12
vendor/github.com/labstack/echo/v4/middleware/csrf_samesite.go
generated
vendored
Normal file
12
vendor/github.com/labstack/echo/v4/middleware/csrf_samesite.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// +build go1.13
|
||||||
|
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SameSiteNoneMode required to be redefined for Go 1.12 support (see #1524)
|
||||||
|
SameSiteNoneMode http.SameSite = http.SameSiteNoneMode
|
||||||
|
)
|
12
vendor/github.com/labstack/echo/v4/middleware/csrf_samesite_1.12.go
generated
vendored
Normal file
12
vendor/github.com/labstack/echo/v4/middleware/csrf_samesite_1.12.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// +build !go1.13
|
||||||
|
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SameSiteNoneMode required to be redefined for Go 1.12 support (see #1524)
|
||||||
|
SameSiteNoneMode http.SameSite = 4
|
||||||
|
)
|
120
vendor/github.com/labstack/echo/v4/middleware/decompress.go
generated
vendored
Normal file
120
vendor/github.com/labstack/echo/v4/middleware/decompress.go
generated
vendored
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// DecompressConfig defines the config for Decompress middleware.
|
||||||
|
DecompressConfig struct {
|
||||||
|
// Skipper defines a function to skip middleware.
|
||||||
|
Skipper Skipper
|
||||||
|
|
||||||
|
// GzipDecompressPool defines an interface to provide the sync.Pool used to create/store Gzip readers
|
||||||
|
GzipDecompressPool Decompressor
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
//GZIPEncoding content-encoding header if set to "gzip", decompress body contents.
|
||||||
|
const GZIPEncoding string = "gzip"
|
||||||
|
|
||||||
|
// Decompressor is used to get the sync.Pool used by the middleware to get Gzip readers
|
||||||
|
type Decompressor interface {
|
||||||
|
gzipDecompressPool() sync.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
//DefaultDecompressConfig defines the config for decompress middleware
|
||||||
|
DefaultDecompressConfig = DecompressConfig{
|
||||||
|
Skipper: DefaultSkipper,
|
||||||
|
GzipDecompressPool: &DefaultGzipDecompressPool{},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultGzipDecompressPool is the default implementation of Decompressor interface
|
||||||
|
type DefaultGzipDecompressPool struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DefaultGzipDecompressPool) gzipDecompressPool() sync.Pool {
|
||||||
|
return sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
// create with an empty reader (but with GZIP header)
|
||||||
|
w, err := gzip.NewWriterLevel(ioutil.Discard, gzip.BestSpeed)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
b := new(bytes.Buffer)
|
||||||
|
w.Reset(b)
|
||||||
|
w.Flush()
|
||||||
|
w.Close()
|
||||||
|
|
||||||
|
r, err := gzip.NewReader(bytes.NewReader(b.Bytes()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Decompress decompresses request body based if content encoding type is set to "gzip" with default config
|
||||||
|
func Decompress() echo.MiddlewareFunc {
|
||||||
|
return DecompressWithConfig(DefaultDecompressConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
//DecompressWithConfig decompresses request body based if content encoding type is set to "gzip" with config
|
||||||
|
func DecompressWithConfig(config DecompressConfig) echo.MiddlewareFunc {
|
||||||
|
// Defaults
|
||||||
|
if config.Skipper == nil {
|
||||||
|
config.Skipper = DefaultGzipConfig.Skipper
|
||||||
|
}
|
||||||
|
if config.GzipDecompressPool == nil {
|
||||||
|
config.GzipDecompressPool = DefaultDecompressConfig.GzipDecompressPool
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
|
pool := config.GzipDecompressPool.gzipDecompressPool()
|
||||||
|
return func(c echo.Context) error {
|
||||||
|
if config.Skipper(c) {
|
||||||
|
return next(c)
|
||||||
|
}
|
||||||
|
switch c.Request().Header.Get(echo.HeaderContentEncoding) {
|
||||||
|
case GZIPEncoding:
|
||||||
|
b := c.Request().Body
|
||||||
|
|
||||||
|
i := pool.Get()
|
||||||
|
gr, ok := i.(*gzip.Reader)
|
||||||
|
if !ok {
|
||||||
|
return echo.NewHTTPError(http.StatusInternalServerError, i.(error).Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := gr.Reset(b); err != nil {
|
||||||
|
pool.Put(gr)
|
||||||
|
if err == io.EOF { //ignore if body is empty
|
||||||
|
return next(c)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
io.Copy(&buf, gr)
|
||||||
|
|
||||||
|
gr.Close()
|
||||||
|
pool.Put(gr)
|
||||||
|
|
||||||
|
b.Close() // http.Request.Body is closed by the Server, but because we are replacing it, it must be closed here
|
||||||
|
|
||||||
|
r := ioutil.NopCloser(&buf)
|
||||||
|
c.Request().Body = r
|
||||||
|
}
|
||||||
|
return next(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
vendor/github.com/labstack/echo/v4/middleware/jwt.go
generated
vendored
19
vendor/github.com/labstack/echo/v4/middleware/jwt.go
generated
vendored
@ -57,6 +57,7 @@ type (
|
|||||||
// - "query:<name>"
|
// - "query:<name>"
|
||||||
// - "param:<name>"
|
// - "param:<name>"
|
||||||
// - "cookie:<name>"
|
// - "cookie:<name>"
|
||||||
|
// - "form:<name>"
|
||||||
TokenLookup string
|
TokenLookup string
|
||||||
|
|
||||||
// AuthScheme to be used in the Authorization header.
|
// AuthScheme to be used in the Authorization header.
|
||||||
@ -86,6 +87,7 @@ const (
|
|||||||
// Errors
|
// Errors
|
||||||
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 (
|
var (
|
||||||
@ -166,6 +168,8 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
|
|||||||
extractor = jwtFromParam(parts[1])
|
extractor = jwtFromParam(parts[1])
|
||||||
case "cookie":
|
case "cookie":
|
||||||
extractor = jwtFromCookie(parts[1])
|
extractor = jwtFromCookie(parts[1])
|
||||||
|
case "form":
|
||||||
|
extractor = jwtFromForm(parts[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
@ -213,8 +217,8 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
|
|||||||
return config.ErrorHandlerWithContext(err, c)
|
return config.ErrorHandlerWithContext(err, c)
|
||||||
}
|
}
|
||||||
return &echo.HTTPError{
|
return &echo.HTTPError{
|
||||||
Code: http.StatusUnauthorized,
|
Code: ErrJWTInvalid.Code,
|
||||||
Message: "invalid or expired jwt",
|
Message: ErrJWTInvalid.Message,
|
||||||
Internal: err,
|
Internal: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -265,3 +269,14 @@ func jwtFromCookie(name string) jwtExtractor {
|
|||||||
return cookie.Value, nil
|
return cookie.Value, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// jwtFromForm returns a `jwtExtractor` that extracts token from the form field.
|
||||||
|
func jwtFromForm(name string) jwtExtractor {
|
||||||
|
return func(c echo.Context) (string, error) {
|
||||||
|
field := c.FormValue(name)
|
||||||
|
if field == "" {
|
||||||
|
return "", ErrJWTMissing
|
||||||
|
}
|
||||||
|
return field, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
43
vendor/github.com/labstack/echo/v4/middleware/middleware.go
generated
vendored
43
vendor/github.com/labstack/echo/v4/middleware/middleware.go
generated
vendored
@ -1,6 +1,8 @@
|
|||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -32,6 +34,47 @@ func captureTokens(pattern *regexp.Regexp, input string) *strings.Replacer {
|
|||||||
return strings.NewReplacer(replace...)
|
return strings.NewReplacer(replace...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func rewriteRulesRegex(rewrite map[string]string) map[*regexp.Regexp]string {
|
||||||
|
// Initialize
|
||||||
|
rulesRegex := map[*regexp.Regexp]string{}
|
||||||
|
for k, v := range rewrite {
|
||||||
|
k = regexp.QuoteMeta(k)
|
||||||
|
k = strings.Replace(k, `\*`, "(.*?)", -1)
|
||||||
|
if strings.HasPrefix(k, `\^`) {
|
||||||
|
k = strings.Replace(k, `\^`, "^", -1)
|
||||||
|
}
|
||||||
|
k = k + "$"
|
||||||
|
rulesRegex[regexp.MustCompile(k)] = v
|
||||||
|
}
|
||||||
|
return rulesRegex
|
||||||
|
}
|
||||||
|
|
||||||
|
func rewritePath(rewriteRegex map[*regexp.Regexp]string, req *http.Request) {
|
||||||
|
for k, v := range rewriteRegex {
|
||||||
|
rawPath := req.URL.RawPath
|
||||||
|
if rawPath != "" {
|
||||||
|
// RawPath is only set when there has been escaping done. In that case Path must be deduced from rewritten RawPath
|
||||||
|
// because encoded Path could match rules that RawPath did not
|
||||||
|
if replacer := captureTokens(k, rawPath); replacer != nil {
|
||||||
|
rawPath = replacer.Replace(v)
|
||||||
|
|
||||||
|
req.URL.RawPath = rawPath
|
||||||
|
req.URL.Path, _ = url.PathUnescape(rawPath)
|
||||||
|
|
||||||
|
return // rewrite only once
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if replacer := captureTokens(k, req.URL.Path); replacer != nil {
|
||||||
|
req.URL.Path = replacer.Replace(v)
|
||||||
|
|
||||||
|
return // rewrite only once
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// DefaultSkipper returns false which processes the middleware.
|
// DefaultSkipper returns false which processes the middleware.
|
||||||
func DefaultSkipper(echo.Context) bool {
|
func DefaultSkipper(echo.Context) bool {
|
||||||
return false
|
return false
|
||||||
|
32
vendor/github.com/labstack/echo/v4/middleware/proxy.go
generated
vendored
32
vendor/github.com/labstack/echo/v4/middleware/proxy.go
generated
vendored
@ -8,7 +8,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@ -37,6 +36,13 @@ type (
|
|||||||
// "/users/*/orders/*": "/user/$1/order/$2",
|
// "/users/*/orders/*": "/user/$1/order/$2",
|
||||||
Rewrite map[string]string
|
Rewrite map[string]string
|
||||||
|
|
||||||
|
// RegexRewrite defines 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.
|
||||||
|
// Example:
|
||||||
|
// "^/old/[0.9]+/": "/new",
|
||||||
|
// "^/api/.+?/(.*)": "/v2/$1",
|
||||||
|
RegexRewrite map[*regexp.Regexp]string
|
||||||
|
|
||||||
// Context key to store selected ProxyTarget into context.
|
// Context key to store selected ProxyTarget into context.
|
||||||
// Optional. Default value "target".
|
// Optional. Default value "target".
|
||||||
ContextKey string
|
ContextKey string
|
||||||
@ -45,7 +51,8 @@ type (
|
|||||||
// Examples: If custom TLS certificates are required.
|
// Examples: If custom TLS certificates are required.
|
||||||
Transport http.RoundTripper
|
Transport http.RoundTripper
|
||||||
|
|
||||||
rewriteRegex map[*regexp.Regexp]string
|
// ModifyResponse defines function to modify response from ProxyTarget.
|
||||||
|
ModifyResponse func(*http.Response) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProxyTarget defines the upstream target.
|
// ProxyTarget defines the upstream target.
|
||||||
@ -203,12 +210,14 @@ func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc {
|
|||||||
if config.Balancer == nil {
|
if config.Balancer == nil {
|
||||||
panic("echo: proxy middleware requires balancer")
|
panic("echo: proxy middleware requires balancer")
|
||||||
}
|
}
|
||||||
config.rewriteRegex = map[*regexp.Regexp]string{}
|
|
||||||
|
|
||||||
// Initialize
|
if config.Rewrite != nil {
|
||||||
for k, v := range config.Rewrite {
|
if config.RegexRewrite == nil {
|
||||||
k = strings.Replace(k, "*", "(\\S*)", -1)
|
config.RegexRewrite = make(map[*regexp.Regexp]string)
|
||||||
config.rewriteRegex[regexp.MustCompile(k)] = v
|
}
|
||||||
|
for k, v := range rewriteRulesRegex(config.Rewrite) {
|
||||||
|
config.RegexRewrite[k] = v
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
@ -222,13 +231,8 @@ func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc {
|
|||||||
tgt := config.Balancer.Next(c)
|
tgt := config.Balancer.Next(c)
|
||||||
c.Set(config.ContextKey, tgt)
|
c.Set(config.ContextKey, tgt)
|
||||||
|
|
||||||
// Rewrite
|
// Set rewrite path and raw path
|
||||||
for k, v := range config.rewriteRegex {
|
rewritePath(config.RegexRewrite, req)
|
||||||
replacer := captureTokens(k, req.URL.Path)
|
|
||||||
if replacer != nil {
|
|
||||||
req.URL.Path = replacer.Replace(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fix header
|
// Fix header
|
||||||
// Basically it's not good practice to unconditionally pass incoming x-real-ip header to upstream.
|
// Basically it's not good practice to unconditionally pass incoming x-real-ip header to upstream.
|
||||||
|
25
vendor/github.com/labstack/echo/v4/middleware/proxy_1_11.go
generated
vendored
25
vendor/github.com/labstack/echo/v4/middleware/proxy_1_11.go
generated
vendored
@ -3,13 +3,22 @@
|
|||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// StatusCodeContextCanceled is a custom HTTP status code for situations
|
||||||
|
// where a client unexpectedly closed the connection to the server.
|
||||||
|
// As there is no standard error code for "client closed connection", but
|
||||||
|
// various well-known HTTP clients and server implement this HTTP code we use
|
||||||
|
// 499 too instead of the more problematic 5xx, which does not allow to detect this situation
|
||||||
|
const StatusCodeContextCanceled = 499
|
||||||
|
|
||||||
func proxyHTTP(tgt *ProxyTarget, c echo.Context, config ProxyConfig) http.Handler {
|
func proxyHTTP(tgt *ProxyTarget, c echo.Context, config ProxyConfig) http.Handler {
|
||||||
proxy := httputil.NewSingleHostReverseProxy(tgt.URL)
|
proxy := httputil.NewSingleHostReverseProxy(tgt.URL)
|
||||||
proxy.ErrorHandler = func(resp http.ResponseWriter, req *http.Request, err error) {
|
proxy.ErrorHandler = func(resp http.ResponseWriter, req *http.Request, err error) {
|
||||||
@ -17,8 +26,22 @@ func proxyHTTP(tgt *ProxyTarget, c echo.Context, config ProxyConfig) http.Handle
|
|||||||
if tgt.Name != "" {
|
if tgt.Name != "" {
|
||||||
desc = fmt.Sprintf("%s(%s)", tgt.Name, tgt.URL.String())
|
desc = fmt.Sprintf("%s(%s)", tgt.Name, tgt.URL.String())
|
||||||
}
|
}
|
||||||
c.Set("_error", echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("remote %s unreachable, could not forward: %v", desc, err)))
|
// If the client canceled the request (usually by closing the connection), we can report a
|
||||||
|
// client error (4xx) instead of a server error (5xx) to correctly identify the situation.
|
||||||
|
// The Go standard library (at of late 2020) wraps the exported, standard
|
||||||
|
// context.Canceled error with unexported garbage value requiring a substring check, see
|
||||||
|
// https://github.com/golang/go/blob/6965b01ea248cabb70c3749fd218b36089a21efb/src/net/net.go#L416-L430
|
||||||
|
if err == context.Canceled || strings.Contains(err.Error(), "operation was canceled") {
|
||||||
|
httpError := echo.NewHTTPError(StatusCodeContextCanceled, fmt.Sprintf("client closed connection: %v", err))
|
||||||
|
httpError.Internal = err
|
||||||
|
c.Set("_error", httpError)
|
||||||
|
} else {
|
||||||
|
httpError := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("remote %s unreachable, could not forward: %v", desc, err))
|
||||||
|
httpError.Internal = err
|
||||||
|
c.Set("_error", httpError)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
proxy.Transport = config.Transport
|
proxy.Transport = config.Transport
|
||||||
|
proxy.ModifyResponse = config.ModifyResponse
|
||||||
return proxy
|
return proxy
|
||||||
}
|
}
|
||||||
|
266
vendor/github.com/labstack/echo/v4/middleware/rate_limiter.go
generated
vendored
Normal file
266
vendor/github.com/labstack/echo/v4/middleware/rate_limiter.go
generated
vendored
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
"golang.org/x/time/rate"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// RateLimiterStore is the interface to be implemented by custom stores.
|
||||||
|
RateLimiterStore interface {
|
||||||
|
// Stores for the rate limiter have to implement the Allow method
|
||||||
|
Allow(identifier string) (bool, error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// RateLimiterConfig defines the configuration for the rate limiter
|
||||||
|
RateLimiterConfig struct {
|
||||||
|
Skipper Skipper
|
||||||
|
BeforeFunc BeforeFunc
|
||||||
|
// IdentifierExtractor uses echo.Context to extract the identifier for a visitor
|
||||||
|
IdentifierExtractor Extractor
|
||||||
|
// Store defines a store for the rate limiter
|
||||||
|
Store RateLimiterStore
|
||||||
|
// ErrorHandler provides a handler to be called when IdentifierExtractor returns an error
|
||||||
|
ErrorHandler func(context echo.Context, err error) error
|
||||||
|
// DenyHandler provides a handler to be called when RateLimiter denies access
|
||||||
|
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
|
||||||
|
var (
|
||||||
|
// ErrRateLimitExceeded denotes an error raised when rate limit is exceeded
|
||||||
|
ErrRateLimitExceeded = echo.NewHTTPError(http.StatusTooManyRequests, "rate limit exceeded")
|
||||||
|
// ErrExtractorError denotes an error raised when extractor function is unsuccessful
|
||||||
|
ErrExtractorError = echo.NewHTTPError(http.StatusForbidden, "error while extracting identifier")
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultRateLimiterConfig defines default values for RateLimiterConfig
|
||||||
|
var DefaultRateLimiterConfig = RateLimiterConfig{
|
||||||
|
Skipper: DefaultSkipper,
|
||||||
|
IdentifierExtractor: func(ctx echo.Context) (string, error) {
|
||||||
|
id := ctx.RealIP()
|
||||||
|
return id, nil
|
||||||
|
},
|
||||||
|
ErrorHandler: func(context echo.Context, err error) error {
|
||||||
|
return &echo.HTTPError{
|
||||||
|
Code: ErrExtractorError.Code,
|
||||||
|
Message: ErrExtractorError.Message,
|
||||||
|
Internal: err,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DenyHandler: func(context echo.Context, identifier string, err error) error {
|
||||||
|
return &echo.HTTPError{
|
||||||
|
Code: ErrRateLimitExceeded.Code,
|
||||||
|
Message: ErrRateLimitExceeded.Message,
|
||||||
|
Internal: err,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
RateLimiter returns a rate limiting middleware
|
||||||
|
|
||||||
|
e := echo.New()
|
||||||
|
|
||||||
|
limiterStore := middleware.NewRateLimiterMemoryStore(20)
|
||||||
|
|
||||||
|
e.GET("/rate-limited", func(c echo.Context) error {
|
||||||
|
return c.String(http.StatusOK, "test")
|
||||||
|
}, RateLimiter(limiterStore))
|
||||||
|
*/
|
||||||
|
func RateLimiter(store RateLimiterStore) echo.MiddlewareFunc {
|
||||||
|
config := DefaultRateLimiterConfig
|
||||||
|
config.Store = store
|
||||||
|
|
||||||
|
return RateLimiterWithConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
RateLimiterWithConfig returns a rate limiting middleware
|
||||||
|
|
||||||
|
e := echo.New()
|
||||||
|
|
||||||
|
config := middleware.RateLimiterConfig{
|
||||||
|
Skipper: DefaultSkipper,
|
||||||
|
Store: middleware.NewRateLimiterMemoryStore(
|
||||||
|
middleware.RateLimiterMemoryStoreConfig{Rate: 10, Burst: 30, ExpiresIn: 3 * time.Minute}
|
||||||
|
)
|
||||||
|
IdentifierExtractor: func(ctx echo.Context) (string, error) {
|
||||||
|
id := ctx.RealIP()
|
||||||
|
return id, nil
|
||||||
|
},
|
||||||
|
ErrorHandler: func(context echo.Context, err error) error {
|
||||||
|
return context.JSON(http.StatusTooManyRequests, nil)
|
||||||
|
},
|
||||||
|
DenyHandler: func(context echo.Context, identifier string) error {
|
||||||
|
return context.JSON(http.StatusForbidden, nil)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
e.GET("/rate-limited", func(c echo.Context) error {
|
||||||
|
return c.String(http.StatusOK, "test")
|
||||||
|
}, middleware.RateLimiterWithConfig(config))
|
||||||
|
*/
|
||||||
|
func RateLimiterWithConfig(config RateLimiterConfig) echo.MiddlewareFunc {
|
||||||
|
if config.Skipper == nil {
|
||||||
|
config.Skipper = DefaultRateLimiterConfig.Skipper
|
||||||
|
}
|
||||||
|
if config.IdentifierExtractor == nil {
|
||||||
|
config.IdentifierExtractor = DefaultRateLimiterConfig.IdentifierExtractor
|
||||||
|
}
|
||||||
|
if config.ErrorHandler == nil {
|
||||||
|
config.ErrorHandler = DefaultRateLimiterConfig.ErrorHandler
|
||||||
|
}
|
||||||
|
if config.DenyHandler == nil {
|
||||||
|
config.DenyHandler = DefaultRateLimiterConfig.DenyHandler
|
||||||
|
}
|
||||||
|
if config.Store == nil {
|
||||||
|
panic("Store configuration must be provided")
|
||||||
|
}
|
||||||
|
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
|
return func(c echo.Context) error {
|
||||||
|
if config.Skipper(c) {
|
||||||
|
return next(c)
|
||||||
|
}
|
||||||
|
if config.BeforeFunc != nil {
|
||||||
|
config.BeforeFunc(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
identifier, err := config.IdentifierExtractor(c)
|
||||||
|
if err != nil {
|
||||||
|
c.Error(config.ErrorHandler(c, err))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if allow, err := config.Store.Allow(identifier); !allow {
|
||||||
|
c.Error(config.DenyHandler(c, identifier, err))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return next(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
// RateLimiterMemoryStore is the built-in store implementation for RateLimiter
|
||||||
|
RateLimiterMemoryStore struct {
|
||||||
|
visitors map[string]*Visitor
|
||||||
|
mutex sync.Mutex
|
||||||
|
rate rate.Limit
|
||||||
|
burst int
|
||||||
|
expiresIn time.Duration
|
||||||
|
lastCleanup time.Time
|
||||||
|
}
|
||||||
|
// Visitor signifies a unique user's limiter details
|
||||||
|
Visitor struct {
|
||||||
|
*rate.Limiter
|
||||||
|
lastSeen time.Time
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
NewRateLimiterMemoryStore returns an instance of RateLimiterMemoryStore with
|
||||||
|
the provided rate (as req/s). Burst and ExpiresIn will be set to default values.
|
||||||
|
|
||||||
|
Example (with 20 requests/sec):
|
||||||
|
|
||||||
|
limiterStore := middleware.NewRateLimiterMemoryStore(20)
|
||||||
|
|
||||||
|
*/
|
||||||
|
func NewRateLimiterMemoryStore(rate rate.Limit) (store *RateLimiterMemoryStore) {
|
||||||
|
return NewRateLimiterMemoryStoreWithConfig(RateLimiterMemoryStoreConfig{
|
||||||
|
Rate: rate,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
NewRateLimiterMemoryStoreWithConfig returns an instance of RateLimiterMemoryStore
|
||||||
|
with the provided configuration. Rate must be provided. Burst will be set to the value of
|
||||||
|
the configured rate if not provided or set to 0.
|
||||||
|
|
||||||
|
The build-in memory store is usually capable for modest loads. For higher loads other
|
||||||
|
store implementations should be considered.
|
||||||
|
|
||||||
|
Characteristics:
|
||||||
|
* Concurrency above 100 parallel requests may causes measurable lock contention
|
||||||
|
* A high number of different IP addresses (above 16000) may be impacted by the internally used Go map
|
||||||
|
* A high number of requests from a single IP address may cause lock contention
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
limiterStore := middleware.NewRateLimiterMemoryStoreWithConfig(
|
||||||
|
middleware.RateLimiterMemoryStoreConfig{Rate: 50, Burst: 200, ExpiresIn: 5 * time.Minutes},
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
func NewRateLimiterMemoryStoreWithConfig(config RateLimiterMemoryStoreConfig) (store *RateLimiterMemoryStore) {
|
||||||
|
store = &RateLimiterMemoryStore{}
|
||||||
|
|
||||||
|
store.rate = config.Rate
|
||||||
|
store.burst = config.Burst
|
||||||
|
store.expiresIn = config.ExpiresIn
|
||||||
|
if config.ExpiresIn == 0 {
|
||||||
|
store.expiresIn = DefaultRateLimiterMemoryStoreConfig.ExpiresIn
|
||||||
|
}
|
||||||
|
if config.Burst == 0 {
|
||||||
|
store.burst = int(config.Rate)
|
||||||
|
}
|
||||||
|
store.visitors = make(map[string]*Visitor)
|
||||||
|
store.lastCleanup = now()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// RateLimiterMemoryStoreConfig represents configuration for RateLimiterMemoryStore
|
||||||
|
type RateLimiterMemoryStoreConfig struct {
|
||||||
|
Rate rate.Limit // Rate of requests allowed to pass as req/s
|
||||||
|
Burst int // Burst additionally allows a number of requests to pass when rate limit is reached
|
||||||
|
ExpiresIn time.Duration // ExpiresIn is the duration after that a rate limiter is cleaned up
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultRateLimiterMemoryStoreConfig provides default configuration values for RateLimiterMemoryStore
|
||||||
|
var DefaultRateLimiterMemoryStoreConfig = RateLimiterMemoryStoreConfig{
|
||||||
|
ExpiresIn: 3 * time.Minute,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow implements RateLimiterStore.Allow
|
||||||
|
func (store *RateLimiterMemoryStore) Allow(identifier string) (bool, error) {
|
||||||
|
store.mutex.Lock()
|
||||||
|
limiter, exists := store.visitors[identifier]
|
||||||
|
if !exists {
|
||||||
|
limiter = new(Visitor)
|
||||||
|
limiter.Limiter = rate.NewLimiter(store.rate, store.burst)
|
||||||
|
store.visitors[identifier] = limiter
|
||||||
|
}
|
||||||
|
limiter.lastSeen = now()
|
||||||
|
if now().Sub(store.lastCleanup) > store.expiresIn {
|
||||||
|
store.cleanupStaleVisitors()
|
||||||
|
}
|
||||||
|
store.mutex.Unlock()
|
||||||
|
return limiter.AllowN(now(), 1), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
cleanupStaleVisitors helps manage the size of the visitors map by removing stale records
|
||||||
|
of users who haven't visited again after the configured expiry time has elapsed
|
||||||
|
*/
|
||||||
|
func (store *RateLimiterMemoryStore) cleanupStaleVisitors() {
|
||||||
|
for id, visitor := range store.visitors {
|
||||||
|
if now().Sub(visitor.lastSeen) > store.expiresIn {
|
||||||
|
delete(store.visitors, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
store.lastCleanup = now()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
actual time method which is mocked in test file
|
||||||
|
*/
|
||||||
|
var now = time.Now
|
22
vendor/github.com/labstack/echo/v4/middleware/recover.go
generated
vendored
22
vendor/github.com/labstack/echo/v4/middleware/recover.go
generated
vendored
@ -5,6 +5,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
|
"github.com/labstack/gommon/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@ -25,6 +26,10 @@ type (
|
|||||||
// DisablePrintStack disables printing stack trace.
|
// DisablePrintStack disables printing stack trace.
|
||||||
// Optional. Default value as false.
|
// Optional. Default value as false.
|
||||||
DisablePrintStack bool `yaml:"disable_print_stack"`
|
DisablePrintStack bool `yaml:"disable_print_stack"`
|
||||||
|
|
||||||
|
// LogLevel is log level to printing stack trace.
|
||||||
|
// Optional. Default value 0 (Print).
|
||||||
|
LogLevel log.Lvl
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -35,6 +40,7 @@ var (
|
|||||||
StackSize: 4 << 10, // 4 KB
|
StackSize: 4 << 10, // 4 KB
|
||||||
DisableStackAll: false,
|
DisableStackAll: false,
|
||||||
DisablePrintStack: false,
|
DisablePrintStack: false,
|
||||||
|
LogLevel: 0,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -70,7 +76,21 @@ func RecoverWithConfig(config RecoverConfig) echo.MiddlewareFunc {
|
|||||||
stack := make([]byte, config.StackSize)
|
stack := make([]byte, config.StackSize)
|
||||||
length := runtime.Stack(stack, !config.DisableStackAll)
|
length := runtime.Stack(stack, !config.DisableStackAll)
|
||||||
if !config.DisablePrintStack {
|
if !config.DisablePrintStack {
|
||||||
c.Logger().Printf("[PANIC RECOVER] %v %s\n", err, stack[:length])
|
msg := fmt.Sprintf("[PANIC RECOVER] %v %s\n", err, stack[:length])
|
||||||
|
switch config.LogLevel {
|
||||||
|
case log.DEBUG:
|
||||||
|
c.Logger().Debug(msg)
|
||||||
|
case log.INFO:
|
||||||
|
c.Logger().Info(msg)
|
||||||
|
case log.WARN:
|
||||||
|
c.Logger().Warn(msg)
|
||||||
|
case log.ERROR:
|
||||||
|
c.Logger().Error(msg)
|
||||||
|
case log.OFF:
|
||||||
|
// None.
|
||||||
|
default:
|
||||||
|
c.Logger().Print(msg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
c.Error(err)
|
c.Error(err)
|
||||||
}
|
}
|
||||||
|
36
vendor/github.com/labstack/echo/v4/middleware/rewrite.go
generated
vendored
36
vendor/github.com/labstack/echo/v4/middleware/rewrite.go
generated
vendored
@ -2,7 +2,6 @@ package middleware
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
@ -23,7 +22,12 @@ type (
|
|||||||
// Required.
|
// Required.
|
||||||
Rules map[string]string `yaml:"rules"`
|
Rules map[string]string `yaml:"rules"`
|
||||||
|
|
||||||
rulesRegex map[*regexp.Regexp]string
|
// 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.
|
||||||
|
// Example:
|
||||||
|
// "^/old/[0.9]+/": "/new",
|
||||||
|
// "^/api/.+?/(.*)": "/v2/$1",
|
||||||
|
RegexRules map[*regexp.Regexp]string `yaml:"regex_rules"`
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -47,20 +51,19 @@ func Rewrite(rules map[string]string) echo.MiddlewareFunc {
|
|||||||
// See: `Rewrite()`.
|
// See: `Rewrite()`.
|
||||||
func RewriteWithConfig(config RewriteConfig) echo.MiddlewareFunc {
|
func RewriteWithConfig(config RewriteConfig) echo.MiddlewareFunc {
|
||||||
// Defaults
|
// Defaults
|
||||||
if config.Rules == nil {
|
if config.Rules == nil && config.RegexRules == nil {
|
||||||
panic("echo: rewrite middleware requires url path rewrite rules")
|
panic("echo: rewrite middleware requires url path rewrite rules or regex rules")
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Skipper == nil {
|
if config.Skipper == nil {
|
||||||
config.Skipper = DefaultBodyDumpConfig.Skipper
|
config.Skipper = DefaultBodyDumpConfig.Skipper
|
||||||
}
|
}
|
||||||
config.rulesRegex = map[*regexp.Regexp]string{}
|
|
||||||
|
|
||||||
// Initialize
|
if config.RegexRules == nil {
|
||||||
for k, v := range config.Rules {
|
config.RegexRules = make(map[*regexp.Regexp]string)
|
||||||
k = regexp.QuoteMeta(k)
|
}
|
||||||
k = strings.Replace(k, `\*`, "(.*)", -1)
|
for k, v := range rewriteRulesRegex(config.Rules) {
|
||||||
k = k + "$"
|
config.RegexRules[k] = v
|
||||||
config.rulesRegex[regexp.MustCompile(k)] = v
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
@ -70,15 +73,8 @@ func RewriteWithConfig(config RewriteConfig) echo.MiddlewareFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
req := c.Request()
|
req := c.Request()
|
||||||
|
// Set rewrite path and raw path
|
||||||
// Rewrite
|
rewritePath(config.RegexRules, req)
|
||||||
for k, v := range config.rulesRegex {
|
|
||||||
replacer := captureTokens(k, req.URL.Path)
|
|
||||||
if replacer != nil {
|
|
||||||
req.URL.Path = replacer.Replace(v)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return next(c)
|
return next(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
13
vendor/github.com/labstack/echo/v4/middleware/slash.go
generated
vendored
13
vendor/github.com/labstack/echo/v4/middleware/slash.go
generated
vendored
@ -60,7 +60,7 @@ func AddTrailingSlashWithConfig(config TrailingSlashConfig) echo.MiddlewareFunc
|
|||||||
|
|
||||||
// Redirect
|
// Redirect
|
||||||
if config.RedirectCode != 0 {
|
if config.RedirectCode != 0 {
|
||||||
return c.Redirect(config.RedirectCode, uri)
|
return c.Redirect(config.RedirectCode, sanitizeURI(uri))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forward
|
// Forward
|
||||||
@ -108,7 +108,7 @@ func RemoveTrailingSlashWithConfig(config TrailingSlashConfig) echo.MiddlewareFu
|
|||||||
|
|
||||||
// Redirect
|
// Redirect
|
||||||
if config.RedirectCode != 0 {
|
if config.RedirectCode != 0 {
|
||||||
return c.Redirect(config.RedirectCode, uri)
|
return c.Redirect(config.RedirectCode, sanitizeURI(uri))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forward
|
// Forward
|
||||||
@ -119,3 +119,12 @@ func RemoveTrailingSlashWithConfig(config TrailingSlashConfig) echo.MiddlewareFu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sanitizeURI(uri string) string {
|
||||||
|
// double slash `\\`, `//` or even `\/` is absolute uri for browsers and by redirecting request to that uri
|
||||||
|
// we are vulnerable to open redirect attack. so replace all slashes from the beginning with single slash
|
||||||
|
if len(uri) > 1 && (uri[0] == '\\' || uri[0] == '/') && (uri[1] == '\\' || uri[1] == '/') {
|
||||||
|
uri = "/" + strings.TrimLeft(uri, `/\`)
|
||||||
|
}
|
||||||
|
return uri
|
||||||
|
}
|
||||||
|
17
vendor/github.com/labstack/echo/v4/middleware/static.go
generated
vendored
17
vendor/github.com/labstack/echo/v4/middleware/static.go
generated
vendored
@ -36,6 +36,12 @@ type (
|
|||||||
// 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.
|
||||||
|
// Example: when assigning a static middleware to a non root path group,
|
||||||
|
// the filesystem path is not doubled
|
||||||
|
// Optional. Default value false.
|
||||||
|
IgnoreBase bool `yaml:"ignoreBase"`
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -161,7 +167,16 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
name := filepath.Join(config.Root, path.Clean("/"+p)) // "/"+ for security
|
name := filepath.Join(config.Root, filepath.Clean("/"+p)) // "/"+ for security
|
||||||
|
|
||||||
|
if config.IgnoreBase {
|
||||||
|
routePath := path.Base(strings.TrimRight(c.Path(), "/*"))
|
||||||
|
baseURLPath := path.Base(p)
|
||||||
|
if baseURLPath == routePath {
|
||||||
|
i := strings.LastIndex(name, routePath)
|
||||||
|
name = name[:i] + strings.Replace(name[i:], routePath, "", 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fi, err := os.Stat(name)
|
fi, err := os.Stat(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
111
vendor/github.com/labstack/echo/v4/middleware/timeout.go
generated
vendored
Normal file
111
vendor/github.com/labstack/echo/v4/middleware/timeout.go
generated
vendored
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
// +build go1.13
|
||||||
|
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// TimeoutConfig defines the config for Timeout middleware.
|
||||||
|
TimeoutConfig struct {
|
||||||
|
// Skipper defines a function to skip middleware.
|
||||||
|
Skipper Skipper
|
||||||
|
|
||||||
|
// ErrorMessage is written to response on timeout in addition to http.StatusServiceUnavailable (503) status code
|
||||||
|
// It can be used to define a custom timeout error message
|
||||||
|
ErrorMessage string
|
||||||
|
|
||||||
|
// OnTimeoutRouteErrorHandler is an error handler that is executed for error that was returned from wrapped route after
|
||||||
|
// request timeouted and we already had sent the error code (503) and message response to the client.
|
||||||
|
// NB: do not write headers/body inside this handler. The response has already been sent to the client and response writer
|
||||||
|
// will not accept anything no more. If you want to know what actual route middleware timeouted use `c.Path()`
|
||||||
|
OnTimeoutRouteErrorHandler func(err error, c echo.Context)
|
||||||
|
|
||||||
|
// Timeout configures a timeout for the middleware, defaults to 0 for no timeout
|
||||||
|
// NOTE: when difference between timeout duration and handler execution time is almost the same (in range of 100microseconds)
|
||||||
|
// the result of timeout does not seem to be reliable - could respond timeout, could respond handler output
|
||||||
|
// difference over 500microseconds (0.5millisecond) response seems to be reliable
|
||||||
|
Timeout time.Duration
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// DefaultTimeoutConfig is the default Timeout middleware config.
|
||||||
|
DefaultTimeoutConfig = TimeoutConfig{
|
||||||
|
Skipper: DefaultSkipper,
|
||||||
|
Timeout: 0,
|
||||||
|
ErrorMessage: "",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Timeout returns a middleware which recovers from panics anywhere in the chain
|
||||||
|
// and handles the control to the centralized HTTPErrorHandler.
|
||||||
|
func Timeout() echo.MiddlewareFunc {
|
||||||
|
return TimeoutWithConfig(DefaultTimeoutConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeoutWithConfig returns a Timeout middleware with config.
|
||||||
|
// See: `Timeout()`.
|
||||||
|
func TimeoutWithConfig(config TimeoutConfig) echo.MiddlewareFunc {
|
||||||
|
// Defaults
|
||||||
|
if config.Skipper == nil {
|
||||||
|
config.Skipper = DefaultTimeoutConfig.Skipper
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
|
return func(c echo.Context) error {
|
||||||
|
if config.Skipper(c) || config.Timeout == 0 {
|
||||||
|
return next(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
handlerWrapper := echoHandlerFuncWrapper{
|
||||||
|
ctx: c,
|
||||||
|
handler: next,
|
||||||
|
errChan: make(chan error, 1),
|
||||||
|
errHandler: config.OnTimeoutRouteErrorHandler,
|
||||||
|
}
|
||||||
|
handler := http.TimeoutHandler(handlerWrapper, config.Timeout, config.ErrorMessage)
|
||||||
|
handler.ServeHTTP(c.Response().Writer, c.Request())
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-handlerWrapper.errChan:
|
||||||
|
return err
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type echoHandlerFuncWrapper struct {
|
||||||
|
ctx echo.Context
|
||||||
|
handler echo.HandlerFunc
|
||||||
|
errHandler func(err error, c echo.Context)
|
||||||
|
errChan chan error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t echoHandlerFuncWrapper) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
// replace writer with TimeoutHandler custom one. This will guarantee that
|
||||||
|
// `writes by h to its ResponseWriter will return ErrHandlerTimeout.`
|
||||||
|
originalWriter := t.ctx.Response().Writer
|
||||||
|
t.ctx.Response().Writer = rw
|
||||||
|
|
||||||
|
err := t.handler(t.ctx)
|
||||||
|
if ctxErr := r.Context().Err(); ctxErr == context.DeadlineExceeded {
|
||||||
|
if err != nil && t.errHandler != nil {
|
||||||
|
t.errHandler(err, t.ctx)
|
||||||
|
}
|
||||||
|
return // on timeout we can not send handler error to client because `http.TimeoutHandler` has already sent headers
|
||||||
|
}
|
||||||
|
// we restore original writer only for cases we did not timeout. On timeout we have already sent response to client
|
||||||
|
// and should not anymore send additional headers/data
|
||||||
|
// so on timeout writer stays what http.TimeoutHandler uses and prevents writing headers/body
|
||||||
|
t.ctx.Response().Writer = originalWriter
|
||||||
|
if err != nil {
|
||||||
|
t.errChan <- err
|
||||||
|
}
|
||||||
|
}
|
4
vendor/github.com/labstack/echo/v4/response.go
generated
vendored
4
vendor/github.com/labstack/echo/v4/response.go
generated
vendored
@ -56,11 +56,11 @@ func (r *Response) WriteHeader(code int) {
|
|||||||
r.echo.Logger.Warn("response already committed")
|
r.echo.Logger.Warn("response already committed")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
r.Status = code
|
||||||
for _, fn := range r.beforeFuncs {
|
for _, fn := range r.beforeFuncs {
|
||||||
fn()
|
fn()
|
||||||
}
|
}
|
||||||
r.Status = code
|
r.Writer.WriteHeader(r.Status)
|
||||||
r.Writer.WriteHeader(code)
|
|
||||||
r.Committed = true
|
r.Committed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
478
vendor/github.com/labstack/echo/v4/router.go
generated
vendored
478
vendor/github.com/labstack/echo/v4/router.go
generated
vendored
@ -2,7 +2,6 @@ package echo
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@ -14,14 +13,16 @@ type (
|
|||||||
echo *Echo
|
echo *Echo
|
||||||
}
|
}
|
||||||
node struct {
|
node struct {
|
||||||
kind kind
|
kind kind
|
||||||
label byte
|
label byte
|
||||||
prefix string
|
prefix string
|
||||||
parent *node
|
parent *node
|
||||||
children children
|
staticChildren children
|
||||||
ppath string
|
ppath string
|
||||||
pnames []string
|
pnames []string
|
||||||
methodHandler *methodHandler
|
methodHandler *methodHandler
|
||||||
|
paramChild *node
|
||||||
|
anyChild *node
|
||||||
}
|
}
|
||||||
kind uint8
|
kind uint8
|
||||||
children []*node
|
children []*node
|
||||||
@ -41,9 +42,12 @@ type (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
skind kind = iota
|
staticKind kind = iota
|
||||||
pkind
|
paramKind
|
||||||
akind
|
anyKind
|
||||||
|
|
||||||
|
paramLabel = byte(':')
|
||||||
|
anyLabel = byte('*')
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewRouter returns a new Router instance.
|
// NewRouter returns a new Router instance.
|
||||||
@ -69,120 +73,147 @@ func (r *Router) Add(method, path string, h HandlerFunc) {
|
|||||||
pnames := []string{} // Param names
|
pnames := []string{} // Param names
|
||||||
ppath := path // Pristine path
|
ppath := path // Pristine path
|
||||||
|
|
||||||
for i, l := 0, len(path); i < l; i++ {
|
for i, lcpIndex := 0, len(path); i < lcpIndex; i++ {
|
||||||
if path[i] == ':' {
|
if path[i] == ':' {
|
||||||
j := i + 1
|
j := i + 1
|
||||||
|
|
||||||
r.insert(method, path[:i], nil, skind, "", nil)
|
r.insert(method, path[:i], nil, staticKind, "", nil)
|
||||||
for ; i < l && path[i] != '/'; i++ {
|
for ; i < lcpIndex && path[i] != '/'; i++ {
|
||||||
}
|
}
|
||||||
|
|
||||||
pnames = append(pnames, path[j:i])
|
pnames = append(pnames, path[j:i])
|
||||||
path = path[:j] + path[i:]
|
path = path[:j] + path[i:]
|
||||||
i, l = j, len(path)
|
i, lcpIndex = j, len(path)
|
||||||
|
|
||||||
if i == l {
|
if i == lcpIndex {
|
||||||
r.insert(method, path[:i], h, pkind, ppath, pnames)
|
r.insert(method, path[:i], h, paramKind, ppath, pnames)
|
||||||
} else {
|
} else {
|
||||||
r.insert(method, path[:i], nil, pkind, "", nil)
|
r.insert(method, path[:i], nil, paramKind, "", nil)
|
||||||
}
|
}
|
||||||
} else if path[i] == '*' {
|
} else if path[i] == '*' {
|
||||||
r.insert(method, path[:i], nil, skind, "", nil)
|
r.insert(method, path[:i], nil, staticKind, "", nil)
|
||||||
pnames = append(pnames, "*")
|
pnames = append(pnames, "*")
|
||||||
r.insert(method, path[:i+1], h, akind, ppath, pnames)
|
r.insert(method, path[:i+1], h, anyKind, ppath, pnames)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r.insert(method, path, h, skind, ppath, pnames)
|
r.insert(method, path, h, staticKind, ppath, pnames)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string, pnames []string) {
|
func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string, pnames []string) {
|
||||||
// Adjust max param
|
// Adjust max param
|
||||||
l := len(pnames)
|
paramLen := len(pnames)
|
||||||
if *r.echo.maxParam < l {
|
if *r.echo.maxParam < paramLen {
|
||||||
*r.echo.maxParam = l
|
*r.echo.maxParam = paramLen
|
||||||
}
|
}
|
||||||
|
|
||||||
cn := r.tree // Current node as root
|
currentNode := r.tree // Current node as root
|
||||||
if cn == nil {
|
if currentNode == nil {
|
||||||
panic("echo: invalid method")
|
panic("echo: invalid method")
|
||||||
}
|
}
|
||||||
search := path
|
search := path
|
||||||
|
|
||||||
for {
|
for {
|
||||||
sl := len(search)
|
searchLen := len(search)
|
||||||
pl := len(cn.prefix)
|
prefixLen := len(currentNode.prefix)
|
||||||
l := 0
|
lcpLen := 0
|
||||||
|
|
||||||
// LCP
|
// LCP - Longest Common Prefix (https://en.wikipedia.org/wiki/LCP_array)
|
||||||
max := pl
|
max := prefixLen
|
||||||
if sl < max {
|
if searchLen < max {
|
||||||
max = sl
|
max = searchLen
|
||||||
}
|
}
|
||||||
for ; l < max && search[l] == cn.prefix[l]; l++ {
|
for ; lcpLen < max && search[lcpLen] == currentNode.prefix[lcpLen]; lcpLen++ {
|
||||||
}
|
}
|
||||||
|
|
||||||
if l == 0 {
|
if lcpLen == 0 {
|
||||||
// At root node
|
// At root node
|
||||||
cn.label = search[0]
|
currentNode.label = search[0]
|
||||||
cn.prefix = search
|
currentNode.prefix = search
|
||||||
if h != nil {
|
if h != nil {
|
||||||
cn.kind = t
|
currentNode.kind = t
|
||||||
cn.addHandler(method, h)
|
currentNode.addHandler(method, h)
|
||||||
cn.ppath = ppath
|
currentNode.ppath = ppath
|
||||||
cn.pnames = pnames
|
currentNode.pnames = pnames
|
||||||
}
|
}
|
||||||
} else if l < pl {
|
} else if lcpLen < prefixLen {
|
||||||
// Split node
|
// Split node
|
||||||
n := newNode(cn.kind, cn.prefix[l:], cn, cn.children, cn.methodHandler, cn.ppath, cn.pnames)
|
n := newNode(
|
||||||
|
currentNode.kind,
|
||||||
|
currentNode.prefix[lcpLen:],
|
||||||
|
currentNode,
|
||||||
|
currentNode.staticChildren,
|
||||||
|
currentNode.methodHandler,
|
||||||
|
currentNode.ppath,
|
||||||
|
currentNode.pnames,
|
||||||
|
currentNode.paramChild,
|
||||||
|
currentNode.anyChild,
|
||||||
|
)
|
||||||
|
|
||||||
// Update parent path for all children to new node
|
// Update parent path for all children to new node
|
||||||
for _, child := range cn.children {
|
for _, child := range currentNode.staticChildren {
|
||||||
child.parent = n
|
child.parent = n
|
||||||
}
|
}
|
||||||
|
if currentNode.paramChild != nil {
|
||||||
|
currentNode.paramChild.parent = n
|
||||||
|
}
|
||||||
|
if currentNode.anyChild != nil {
|
||||||
|
currentNode.anyChild.parent = n
|
||||||
|
}
|
||||||
|
|
||||||
// Reset parent node
|
// Reset parent node
|
||||||
cn.kind = skind
|
currentNode.kind = staticKind
|
||||||
cn.label = cn.prefix[0]
|
currentNode.label = currentNode.prefix[0]
|
||||||
cn.prefix = cn.prefix[:l]
|
currentNode.prefix = currentNode.prefix[:lcpLen]
|
||||||
cn.children = nil
|
currentNode.staticChildren = nil
|
||||||
cn.methodHandler = new(methodHandler)
|
currentNode.methodHandler = new(methodHandler)
|
||||||
cn.ppath = ""
|
currentNode.ppath = ""
|
||||||
cn.pnames = nil
|
currentNode.pnames = nil
|
||||||
|
currentNode.paramChild = nil
|
||||||
|
currentNode.anyChild = nil
|
||||||
|
|
||||||
cn.addChild(n)
|
// Only Static children could reach here
|
||||||
|
currentNode.addStaticChild(n)
|
||||||
|
|
||||||
if l == sl {
|
if lcpLen == searchLen {
|
||||||
// At parent node
|
// At parent node
|
||||||
cn.kind = t
|
currentNode.kind = t
|
||||||
cn.addHandler(method, h)
|
currentNode.addHandler(method, h)
|
||||||
cn.ppath = ppath
|
currentNode.ppath = ppath
|
||||||
cn.pnames = pnames
|
currentNode.pnames = pnames
|
||||||
} else {
|
} else {
|
||||||
// Create child node
|
// Create child node
|
||||||
n = newNode(t, search[l:], cn, nil, new(methodHandler), ppath, pnames)
|
n = newNode(t, search[lcpLen:], currentNode, nil, new(methodHandler), ppath, pnames, nil, nil)
|
||||||
n.addHandler(method, h)
|
n.addHandler(method, h)
|
||||||
cn.addChild(n)
|
// Only Static children could reach here
|
||||||
|
currentNode.addStaticChild(n)
|
||||||
}
|
}
|
||||||
} else if l < sl {
|
} else if lcpLen < searchLen {
|
||||||
search = search[l:]
|
search = search[lcpLen:]
|
||||||
c := cn.findChildWithLabel(search[0])
|
c := currentNode.findChildWithLabel(search[0])
|
||||||
if c != nil {
|
if c != nil {
|
||||||
// Go deeper
|
// Go deeper
|
||||||
cn = c
|
currentNode = c
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Create child node
|
// Create child node
|
||||||
n := newNode(t, search, cn, nil, new(methodHandler), ppath, pnames)
|
n := newNode(t, search, currentNode, nil, new(methodHandler), ppath, pnames, nil, nil)
|
||||||
n.addHandler(method, h)
|
n.addHandler(method, h)
|
||||||
cn.addChild(n)
|
switch t {
|
||||||
|
case staticKind:
|
||||||
|
currentNode.addStaticChild(n)
|
||||||
|
case paramKind:
|
||||||
|
currentNode.paramChild = n
|
||||||
|
case anyKind:
|
||||||
|
currentNode.anyChild = n
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Node already exists
|
// Node already exists
|
||||||
if h != nil {
|
if h != nil {
|
||||||
cn.addHandler(method, h)
|
currentNode.addHandler(method, h)
|
||||||
cn.ppath = ppath
|
currentNode.ppath = ppath
|
||||||
if len(cn.pnames) == 0 { // Issue #729
|
if len(currentNode.pnames) == 0 { // Issue #729
|
||||||
cn.pnames = pnames
|
currentNode.pnames = pnames
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -190,34 +221,27 @@ func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newNode(t kind, pre string, p *node, c children, mh *methodHandler, ppath string, pnames []string) *node {
|
func newNode(t kind, pre string, p *node, sc children, mh *methodHandler, ppath string, pnames []string, paramChildren, anyChildren *node) *node {
|
||||||
return &node{
|
return &node{
|
||||||
kind: t,
|
kind: t,
|
||||||
label: pre[0],
|
label: pre[0],
|
||||||
prefix: pre,
|
prefix: pre,
|
||||||
parent: p,
|
parent: p,
|
||||||
children: c,
|
staticChildren: sc,
|
||||||
ppath: ppath,
|
ppath: ppath,
|
||||||
pnames: pnames,
|
pnames: pnames,
|
||||||
methodHandler: mh,
|
methodHandler: mh,
|
||||||
|
paramChild: paramChildren,
|
||||||
|
anyChild: anyChildren,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *node) addChild(c *node) {
|
func (n *node) addStaticChild(c *node) {
|
||||||
n.children = append(n.children, c)
|
n.staticChildren = append(n.staticChildren, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *node) findChild(l byte, t kind) *node {
|
func (n *node) findStaticChild(l byte) *node {
|
||||||
for _, c := range n.children {
|
for _, c := range n.staticChildren {
|
||||||
if c.label == l && c.kind == t {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *node) findChildWithLabel(l byte) *node {
|
|
||||||
for _, c := range n.children {
|
|
||||||
if c.label == l {
|
if c.label == l {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
@ -225,12 +249,18 @@ func (n *node) findChildWithLabel(l byte) *node {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *node) findChildByKind(t kind) *node {
|
func (n *node) findChildWithLabel(l byte) *node {
|
||||||
for _, c := range n.children {
|
for _, c := range n.staticChildren {
|
||||||
if c.kind == t {
|
if c.label == l {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if l == paramLabel {
|
||||||
|
return n.paramChild
|
||||||
|
}
|
||||||
|
if l == anyLabel {
|
||||||
|
return n.anyChild
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,176 +340,152 @@ func (n *node) checkMethodNotAllowed() HandlerFunc {
|
|||||||
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
|
ctx.path = path
|
||||||
cn := r.tree // Current node as root
|
currentNode := r.tree // Current node as root
|
||||||
|
|
||||||
var (
|
var (
|
||||||
search = path
|
// search stores the remaining path to check for match. By each iteration we move from start of path to end of the path
|
||||||
child *node // Child node
|
// and search value gets shorter and shorter.
|
||||||
n int // Param counter
|
search = path
|
||||||
nk kind // Next kind
|
searchIndex = 0
|
||||||
nn *node // Next node
|
paramIndex int // Param counter
|
||||||
ns string // Next search
|
paramValues = ctx.pvalues // Use the internal slice so the interface can keep the illusion of a dynamic slice
|
||||||
pvalues = ctx.pvalues // Use the internal slice so the interface can keep the illusion of a dynamic slice
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Search order static > param > any
|
// Backtracking is needed when a dead end (leaf node) is reached in the router tree.
|
||||||
|
// To backtrack the current node will be changed to the parent node and the next kind for the
|
||||||
|
// router logic will be returned based on fromKind or kind of the dead end node (static > param > any).
|
||||||
|
// For example if there is no static node match we should check parent next sibling by kind (param).
|
||||||
|
// Backtracking itself does not check if there is a next sibling, this is done by the router logic.
|
||||||
|
backtrackToNextNodeKind := func(fromKind kind) (nextNodeKind kind, valid bool) {
|
||||||
|
previous := currentNode
|
||||||
|
currentNode = previous.parent
|
||||||
|
valid = currentNode != nil
|
||||||
|
|
||||||
|
// Next node type by priority
|
||||||
|
// NOTE: With the current implementation we never backtrack from an `any` route, so `previous.kind` is
|
||||||
|
// always `static` or `any`
|
||||||
|
// If this is changed then for any route next kind would be `static` and this statement should be changed
|
||||||
|
nextNodeKind = previous.kind + 1
|
||||||
|
|
||||||
|
if fromKind == staticKind {
|
||||||
|
// when backtracking is done from static kind block we did not change search so nothing to restore
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore search to value it was before we move to current node we are backtracking from.
|
||||||
|
if previous.kind == staticKind {
|
||||||
|
searchIndex -= len(previous.prefix)
|
||||||
|
} else {
|
||||||
|
paramIndex--
|
||||||
|
// for param/any node.prefix value is always `:` so we can not deduce searchIndex from that and must use pValue
|
||||||
|
// for that index as it would also contain part of path we cut off before moving into node we are backtracking from
|
||||||
|
searchIndex -= len(paramValues[paramIndex])
|
||||||
|
}
|
||||||
|
search = path[searchIndex:]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Router tree is implemented by longest common prefix array (LCP array) https://en.wikipedia.org/wiki/LCP_array
|
||||||
|
// Tree search is implemented as for loop where one loop iteration is divided into 3 separate blocks
|
||||||
|
// Each of these blocks checks specific kind of node (static/param/any). Order of blocks reflex their priority in routing.
|
||||||
|
// Search order/priority is: static > param > any.
|
||||||
|
//
|
||||||
|
// Note: backtracking in tree is implemented by replacing/switching currentNode to previous node
|
||||||
|
// and hoping to (goto statement) next block by priority to check if it is the match.
|
||||||
for {
|
for {
|
||||||
if search == "" {
|
prefixLen := 0 // Prefix length
|
||||||
|
lcpLen := 0 // LCP (longest common prefix) length
|
||||||
|
|
||||||
|
if currentNode.kind == staticKind {
|
||||||
|
searchLen := len(search)
|
||||||
|
prefixLen = len(currentNode.prefix)
|
||||||
|
|
||||||
|
// LCP - Longest Common Prefix (https://en.wikipedia.org/wiki/LCP_array)
|
||||||
|
max := prefixLen
|
||||||
|
if searchLen < max {
|
||||||
|
max = searchLen
|
||||||
|
}
|
||||||
|
for ; lcpLen < max && search[lcpLen] == currentNode.prefix[lcpLen]; lcpLen++ {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if lcpLen != prefixLen {
|
||||||
|
// No matching prefix, let's backtrack to the first possible alternative node of the decision path
|
||||||
|
nk, ok := backtrackToNextNodeKind(staticKind)
|
||||||
|
if !ok {
|
||||||
|
return // No other possibilities on the decision path
|
||||||
|
} else if nk == paramKind {
|
||||||
|
goto Param
|
||||||
|
// NOTE: this case (backtracking from static node to previous any node) can not happen by current any matching logic. Any node is end of search currently
|
||||||
|
//} else if nk == anyKind {
|
||||||
|
// goto Any
|
||||||
|
} else {
|
||||||
|
// Not found (this should never be possible for static node we are looking currently)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The full prefix has matched, remove the prefix from the remaining search
|
||||||
|
search = search[lcpLen:]
|
||||||
|
searchIndex = searchIndex + lcpLen
|
||||||
|
|
||||||
|
// Finish routing if no remaining search and we are on an leaf node
|
||||||
|
if search == "" && currentNode.ppath != "" {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
pl := 0 // Prefix length
|
|
||||||
l := 0 // LCP length
|
|
||||||
|
|
||||||
if cn.label != ':' {
|
|
||||||
sl := len(search)
|
|
||||||
pl = len(cn.prefix)
|
|
||||||
|
|
||||||
// LCP
|
|
||||||
max := pl
|
|
||||||
if sl < max {
|
|
||||||
max = sl
|
|
||||||
}
|
|
||||||
for ; l < max && search[l] == cn.prefix[l]; l++ {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if l == pl {
|
|
||||||
// Continue search
|
|
||||||
search = search[l:]
|
|
||||||
// Finish routing if no remaining search and we are on an leaf node
|
|
||||||
if search == "" && (nn == nil || cn.parent == nil || cn.ppath != "") {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to go back up the tree on no matching prefix or no remaining search
|
|
||||||
if l != pl || search == "" {
|
|
||||||
if nn == nil { // Issue #1348
|
|
||||||
return // Not found
|
|
||||||
}
|
|
||||||
cn = nn
|
|
||||||
search = ns
|
|
||||||
if nk == pkind {
|
|
||||||
goto Param
|
|
||||||
} else if nk == akind {
|
|
||||||
goto Any
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Static node
|
// Static node
|
||||||
if child = cn.findChild(search[0], skind); child != nil {
|
if search != "" {
|
||||||
// Save next
|
if child := currentNode.findStaticChild(search[0]); child != nil {
|
||||||
if cn.prefix[len(cn.prefix)-1] == '/' { // Issue #623
|
currentNode = child
|
||||||
nk = pkind
|
continue
|
||||||
nn = cn
|
|
||||||
ns = search
|
|
||||||
}
|
}
|
||||||
cn = child
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Param:
|
Param:
|
||||||
// Param node
|
// Param node
|
||||||
if child = cn.findChildByKind(pkind); child != nil {
|
if child := currentNode.paramChild; search != "" && child != nil {
|
||||||
// Issue #378
|
currentNode = child
|
||||||
if len(pvalues) == n {
|
// FIXME: when param node does not have any children then param node should act similarly to any node - consider all remaining search as match
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save next
|
|
||||||
if cn.prefix[len(cn.prefix)-1] == '/' { // Issue #623
|
|
||||||
nk = akind
|
|
||||||
nn = cn
|
|
||||||
ns = search
|
|
||||||
}
|
|
||||||
|
|
||||||
cn = child
|
|
||||||
i, l := 0, len(search)
|
i, l := 0, len(search)
|
||||||
for ; i < l && search[i] != '/'; i++ {
|
for ; i < l && search[i] != '/'; i++ {
|
||||||
}
|
}
|
||||||
pvalues[n] = search[:i]
|
paramValues[paramIndex] = search[:i]
|
||||||
n++
|
paramIndex++
|
||||||
search = search[i:]
|
search = search[i:]
|
||||||
|
searchIndex = searchIndex + i
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
Any:
|
Any:
|
||||||
// Any node
|
// Any node
|
||||||
if cn = cn.findChildByKind(akind); cn != nil {
|
if child := currentNode.anyChild; child != nil {
|
||||||
// If any node is found, use remaining path for pvalues
|
// If any node is found, use remaining path for paramValues
|
||||||
pvalues[len(cn.pnames)-1] = search
|
currentNode = child
|
||||||
|
paramValues[len(currentNode.pnames)-1] = search
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// No node found, continue at stored next node
|
// Let's backtrack to the first possible alternative node of the decision path
|
||||||
// or find nearest "any" route
|
nk, ok := backtrackToNextNodeKind(anyKind)
|
||||||
if nn != nil {
|
if !ok {
|
||||||
// No next node to go down in routing (issue #954)
|
return // No other possibilities on the decision path
|
||||||
// Find nearest "any" route going up the routing tree
|
} else if nk == paramKind {
|
||||||
search = ns
|
goto Param
|
||||||
np := nn.parent
|
} else if nk == anyKind {
|
||||||
// Consider param route one level up only
|
goto Any
|
||||||
if cn = nn.findChildByKind(pkind); cn != nil {
|
} else {
|
||||||
pos := strings.IndexByte(ns, '/')
|
// Not found
|
||||||
if pos == -1 {
|
|
||||||
// If no slash is remaining in search string set param value
|
|
||||||
pvalues[len(cn.pnames)-1] = search
|
|
||||||
break
|
|
||||||
} else if pos > 0 {
|
|
||||||
// Otherwise continue route processing with restored next node
|
|
||||||
cn = nn
|
|
||||||
nn = nil
|
|
||||||
ns = ""
|
|
||||||
goto Param
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// No param route found, try to resolve nearest any route
|
|
||||||
for {
|
|
||||||
np = nn.parent
|
|
||||||
if cn = nn.findChildByKind(akind); cn != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if np == nil {
|
|
||||||
break // no further parent nodes in tree, abort
|
|
||||||
}
|
|
||||||
var str strings.Builder
|
|
||||||
str.WriteString(nn.prefix)
|
|
||||||
str.WriteString(search)
|
|
||||||
search = str.String()
|
|
||||||
nn = np
|
|
||||||
}
|
|
||||||
if cn != nil { // use the found "any" route and update path
|
|
||||||
pvalues[len(cn.pnames)-1] = search
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return // Not found
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.handler = cn.findHandler(method)
|
|
||||||
ctx.path = cn.ppath
|
|
||||||
ctx.pnames = cn.pnames
|
|
||||||
|
|
||||||
// NOTE: Slow zone...
|
|
||||||
if ctx.handler == nil {
|
|
||||||
ctx.handler = cn.checkMethodNotAllowed()
|
|
||||||
|
|
||||||
// Dig further for any, might have an empty value for *, e.g.
|
|
||||||
// serving a directory. Issue #207.
|
|
||||||
if cn = cn.findChildByKind(akind); cn == nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if h := cn.findHandler(method); h != nil {
|
|
||||||
ctx.handler = h
|
|
||||||
} else {
|
|
||||||
ctx.handler = cn.checkMethodNotAllowed()
|
|
||||||
}
|
|
||||||
ctx.path = cn.ppath
|
|
||||||
ctx.pnames = cn.pnames
|
|
||||||
pvalues[len(cn.pnames)-1] = ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx.handler = currentNode.findHandler(method)
|
||||||
|
ctx.path = currentNode.ppath
|
||||||
|
ctx.pnames = currentNode.pnames
|
||||||
|
|
||||||
|
if ctx.handler == nil {
|
||||||
|
ctx.handler = currentNode.checkMethodNotAllowed()
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
2
vendor/github.com/lib/pq/.gitignore
generated
vendored
2
vendor/github.com/lib/pq/.gitignore
generated
vendored
@ -2,3 +2,5 @@
|
|||||||
*.test
|
*.test
|
||||||
*~
|
*~
|
||||||
*.swp
|
*.swp
|
||||||
|
.idea
|
||||||
|
.vscode
|
3
vendor/github.com/lib/pq/.travis.yml
generated
vendored
3
vendor/github.com/lib/pq/.travis.yml
generated
vendored
@ -1,8 +1,8 @@
|
|||||||
language: go
|
language: go
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- 1.13.x
|
|
||||||
- 1.14.x
|
- 1.14.x
|
||||||
|
- 1.15.x
|
||||||
- master
|
- master
|
||||||
|
|
||||||
sudo: true
|
sudo: true
|
||||||
@ -13,6 +13,7 @@ env:
|
|||||||
- PQGOSSLTESTS=1
|
- PQGOSSLTESTS=1
|
||||||
- PQSSLCERTTEST_PATH=$PWD/certs
|
- PQSSLCERTTEST_PATH=$PWD/certs
|
||||||
- PGHOST=127.0.0.1
|
- PGHOST=127.0.0.1
|
||||||
|
- GODEBUG=x509ignoreCN=0
|
||||||
matrix:
|
matrix:
|
||||||
- PGVERSION=10
|
- PGVERSION=10
|
||||||
- PGVERSION=9.6
|
- PGVERSION=9.6
|
||||||
|
139
vendor/github.com/lib/pq/array.go
generated
vendored
139
vendor/github.com/lib/pq/array.go
generated
vendored
@ -35,19 +35,31 @@ func Array(a interface{}) interface {
|
|||||||
return (*BoolArray)(&a)
|
return (*BoolArray)(&a)
|
||||||
case []float64:
|
case []float64:
|
||||||
return (*Float64Array)(&a)
|
return (*Float64Array)(&a)
|
||||||
|
case []float32:
|
||||||
|
return (*Float32Array)(&a)
|
||||||
case []int64:
|
case []int64:
|
||||||
return (*Int64Array)(&a)
|
return (*Int64Array)(&a)
|
||||||
|
case []int32:
|
||||||
|
return (*Int32Array)(&a)
|
||||||
case []string:
|
case []string:
|
||||||
return (*StringArray)(&a)
|
return (*StringArray)(&a)
|
||||||
|
case [][]byte:
|
||||||
|
return (*ByteaArray)(&a)
|
||||||
|
|
||||||
case *[]bool:
|
case *[]bool:
|
||||||
return (*BoolArray)(a)
|
return (*BoolArray)(a)
|
||||||
case *[]float64:
|
case *[]float64:
|
||||||
return (*Float64Array)(a)
|
return (*Float64Array)(a)
|
||||||
|
case *[]float32:
|
||||||
|
return (*Float32Array)(a)
|
||||||
case *[]int64:
|
case *[]int64:
|
||||||
return (*Int64Array)(a)
|
return (*Int64Array)(a)
|
||||||
|
case *[]int32:
|
||||||
|
return (*Int32Array)(a)
|
||||||
case *[]string:
|
case *[]string:
|
||||||
return (*StringArray)(a)
|
return (*StringArray)(a)
|
||||||
|
case *[][]byte:
|
||||||
|
return (*ByteaArray)(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
return GenericArray{a}
|
return GenericArray{a}
|
||||||
@ -267,6 +279,70 @@ func (a Float64Array) Value() (driver.Value, error) {
|
|||||||
return "{}", nil
|
return "{}", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Float32Array represents a one-dimensional array of the PostgreSQL double
|
||||||
|
// precision type.
|
||||||
|
type Float32Array []float32
|
||||||
|
|
||||||
|
// Scan implements the sql.Scanner interface.
|
||||||
|
func (a *Float32Array) Scan(src interface{}) error {
|
||||||
|
switch src := src.(type) {
|
||||||
|
case []byte:
|
||||||
|
return a.scanBytes(src)
|
||||||
|
case string:
|
||||||
|
return a.scanBytes([]byte(src))
|
||||||
|
case nil:
|
||||||
|
*a = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("pq: cannot convert %T to Float32Array", src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Float32Array) scanBytes(src []byte) error {
|
||||||
|
elems, err := scanLinearArray(src, []byte{','}, "Float32Array")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if *a != nil && len(elems) == 0 {
|
||||||
|
*a = (*a)[:0]
|
||||||
|
} else {
|
||||||
|
b := make(Float32Array, len(elems))
|
||||||
|
for i, v := range elems {
|
||||||
|
var x float64
|
||||||
|
if x, err = strconv.ParseFloat(string(v), 32); err != nil {
|
||||||
|
return fmt.Errorf("pq: parsing array element index %d: %v", i, err)
|
||||||
|
}
|
||||||
|
b[i] = float32(x)
|
||||||
|
}
|
||||||
|
*a = b
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements the driver.Valuer interface.
|
||||||
|
func (a Float32Array) Value() (driver.Value, error) {
|
||||||
|
if a == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if n := len(a); n > 0 {
|
||||||
|
// There will be at least two curly brackets, N bytes of values,
|
||||||
|
// and N-1 bytes of delimiters.
|
||||||
|
b := make([]byte, 1, 1+2*n)
|
||||||
|
b[0] = '{'
|
||||||
|
|
||||||
|
b = strconv.AppendFloat(b, float64(a[0]), 'f', -1, 32)
|
||||||
|
for i := 1; i < n; i++ {
|
||||||
|
b = append(b, ',')
|
||||||
|
b = strconv.AppendFloat(b, float64(a[i]), 'f', -1, 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(append(b, '}')), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "{}", nil
|
||||||
|
}
|
||||||
|
|
||||||
// GenericArray implements the driver.Valuer and sql.Scanner interfaces for
|
// GenericArray implements the driver.Valuer and sql.Scanner interfaces for
|
||||||
// an array or slice of any dimension.
|
// an array or slice of any dimension.
|
||||||
type GenericArray struct{ A interface{} }
|
type GenericArray struct{ A interface{} }
|
||||||
@ -483,6 +559,69 @@ func (a Int64Array) Value() (driver.Value, error) {
|
|||||||
return "{}", nil
|
return "{}", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Int32Array represents a one-dimensional array of the PostgreSQL integer types.
|
||||||
|
type Int32Array []int32
|
||||||
|
|
||||||
|
// Scan implements the sql.Scanner interface.
|
||||||
|
func (a *Int32Array) Scan(src interface{}) error {
|
||||||
|
switch src := src.(type) {
|
||||||
|
case []byte:
|
||||||
|
return a.scanBytes(src)
|
||||||
|
case string:
|
||||||
|
return a.scanBytes([]byte(src))
|
||||||
|
case nil:
|
||||||
|
*a = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("pq: cannot convert %T to Int32Array", src)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Int32Array) scanBytes(src []byte) error {
|
||||||
|
elems, err := scanLinearArray(src, []byte{','}, "Int32Array")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if *a != nil && len(elems) == 0 {
|
||||||
|
*a = (*a)[:0]
|
||||||
|
} else {
|
||||||
|
b := make(Int32Array, len(elems))
|
||||||
|
for i, v := range elems {
|
||||||
|
var x int
|
||||||
|
if x, err = strconv.Atoi(string(v)); err != nil {
|
||||||
|
return fmt.Errorf("pq: parsing array element index %d: %v", i, err)
|
||||||
|
}
|
||||||
|
b[i] = int32(x)
|
||||||
|
}
|
||||||
|
*a = b
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements the driver.Valuer interface.
|
||||||
|
func (a Int32Array) Value() (driver.Value, error) {
|
||||||
|
if a == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if n := len(a); n > 0 {
|
||||||
|
// There will be at least two curly brackets, N bytes of values,
|
||||||
|
// and N-1 bytes of delimiters.
|
||||||
|
b := make([]byte, 1, 1+2*n)
|
||||||
|
b[0] = '{'
|
||||||
|
|
||||||
|
b = strconv.AppendInt(b, int64(a[0]), 10)
|
||||||
|
for i := 1; i < n; i++ {
|
||||||
|
b = append(b, ',')
|
||||||
|
b = strconv.AppendInt(b, int64(a[i]), 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(append(b, '}')), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "{}", nil
|
||||||
|
}
|
||||||
|
|
||||||
// StringArray represents a one-dimensional array of the PostgreSQL character types.
|
// StringArray represents a one-dimensional array of the PostgreSQL character types.
|
||||||
type StringArray []string
|
type StringArray []string
|
||||||
|
|
||||||
|
114
vendor/github.com/lib/pq/conn.go
generated
vendored
114
vendor/github.com/lib/pq/conn.go
generated
vendored
@ -18,6 +18,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
@ -38,13 +39,18 @@ var (
|
|||||||
errNoLastInsertID = errors.New("no LastInsertId available after the empty statement")
|
errNoLastInsertID = errors.New("no LastInsertId available after the empty statement")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Compile time validation that our types implement the expected interfaces
|
||||||
|
var (
|
||||||
|
_ driver.Driver = Driver{}
|
||||||
|
)
|
||||||
|
|
||||||
// Driver is the Postgres database driver.
|
// Driver is the Postgres database driver.
|
||||||
type Driver struct{}
|
type Driver struct{}
|
||||||
|
|
||||||
// Open opens a new connection to the database. name is a connection string.
|
// Open opens a new connection to the database. name is a connection string.
|
||||||
// Most users should only use it through database/sql package from the standard
|
// Most users should only use it through database/sql package from the standard
|
||||||
// library.
|
// library.
|
||||||
func (d *Driver) Open(name string) (driver.Conn, error) {
|
func (d Driver) Open(name string) (driver.Conn, error) {
|
||||||
return Open(name)
|
return Open(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,7 +142,7 @@ type conn struct {
|
|||||||
|
|
||||||
// If true, this connection is bad and all public-facing functions should
|
// If true, this connection is bad and all public-facing functions should
|
||||||
// return ErrBadConn.
|
// return ErrBadConn.
|
||||||
bad bool
|
bad *atomic.Value
|
||||||
|
|
||||||
// If set, this connection should never use the binary format when
|
// If set, this connection should never use the binary format when
|
||||||
// receiving query results from prepared statements. Only provided for
|
// receiving query results from prepared statements. Only provided for
|
||||||
@ -294,9 +300,12 @@ func (c *Connector) open(ctx context.Context) (cn *conn, err error) {
|
|||||||
|
|
||||||
o := c.opts
|
o := c.opts
|
||||||
|
|
||||||
|
bad := &atomic.Value{}
|
||||||
|
bad.Store(false)
|
||||||
cn = &conn{
|
cn = &conn{
|
||||||
opts: o,
|
opts: o,
|
||||||
dialer: c.dialer,
|
dialer: c.dialer,
|
||||||
|
bad: bad,
|
||||||
}
|
}
|
||||||
err = cn.handleDriverSettings(o)
|
err = cn.handleDriverSettings(o)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -501,9 +510,22 @@ func (cn *conn) isInTransaction() bool {
|
|||||||
cn.txnStatus == txnStatusInFailedTransaction
|
cn.txnStatus == txnStatusInFailedTransaction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cn *conn) setBad() {
|
||||||
|
if cn.bad != nil {
|
||||||
|
cn.bad.Store(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cn *conn) getBad() bool {
|
||||||
|
if cn.bad != nil {
|
||||||
|
return cn.bad.Load().(bool)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (cn *conn) checkIsInTransaction(intxn bool) {
|
func (cn *conn) checkIsInTransaction(intxn bool) {
|
||||||
if cn.isInTransaction() != intxn {
|
if cn.isInTransaction() != intxn {
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
errorf("unexpected transaction status %v", cn.txnStatus)
|
errorf("unexpected transaction status %v", cn.txnStatus)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -513,7 +535,7 @@ func (cn *conn) Begin() (_ driver.Tx, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cn *conn) begin(mode string) (_ driver.Tx, err error) {
|
func (cn *conn) begin(mode string) (_ driver.Tx, err error) {
|
||||||
if cn.bad {
|
if cn.getBad() {
|
||||||
return nil, driver.ErrBadConn
|
return nil, driver.ErrBadConn
|
||||||
}
|
}
|
||||||
defer cn.errRecover(&err)
|
defer cn.errRecover(&err)
|
||||||
@ -524,11 +546,11 @@ func (cn *conn) begin(mode string) (_ driver.Tx, err error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if commandTag != "BEGIN" {
|
if commandTag != "BEGIN" {
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
return nil, fmt.Errorf("unexpected command tag %s", commandTag)
|
return nil, fmt.Errorf("unexpected command tag %s", commandTag)
|
||||||
}
|
}
|
||||||
if cn.txnStatus != txnStatusIdleInTransaction {
|
if cn.txnStatus != txnStatusIdleInTransaction {
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
return nil, fmt.Errorf("unexpected transaction status %v", cn.txnStatus)
|
return nil, fmt.Errorf("unexpected transaction status %v", cn.txnStatus)
|
||||||
}
|
}
|
||||||
return cn, nil
|
return cn, nil
|
||||||
@ -542,7 +564,7 @@ func (cn *conn) closeTxn() {
|
|||||||
|
|
||||||
func (cn *conn) Commit() (err error) {
|
func (cn *conn) Commit() (err error) {
|
||||||
defer cn.closeTxn()
|
defer cn.closeTxn()
|
||||||
if cn.bad {
|
if cn.getBad() {
|
||||||
return driver.ErrBadConn
|
return driver.ErrBadConn
|
||||||
}
|
}
|
||||||
defer cn.errRecover(&err)
|
defer cn.errRecover(&err)
|
||||||
@ -564,12 +586,12 @@ func (cn *conn) Commit() (err error) {
|
|||||||
_, commandTag, err := cn.simpleExec("COMMIT")
|
_, commandTag, err := cn.simpleExec("COMMIT")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if cn.isInTransaction() {
|
if cn.isInTransaction() {
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if commandTag != "COMMIT" {
|
if commandTag != "COMMIT" {
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
return fmt.Errorf("unexpected command tag %s", commandTag)
|
return fmt.Errorf("unexpected command tag %s", commandTag)
|
||||||
}
|
}
|
||||||
cn.checkIsInTransaction(false)
|
cn.checkIsInTransaction(false)
|
||||||
@ -578,7 +600,7 @@ func (cn *conn) Commit() (err error) {
|
|||||||
|
|
||||||
func (cn *conn) Rollback() (err error) {
|
func (cn *conn) Rollback() (err error) {
|
||||||
defer cn.closeTxn()
|
defer cn.closeTxn()
|
||||||
if cn.bad {
|
if cn.getBad() {
|
||||||
return driver.ErrBadConn
|
return driver.ErrBadConn
|
||||||
}
|
}
|
||||||
defer cn.errRecover(&err)
|
defer cn.errRecover(&err)
|
||||||
@ -590,7 +612,7 @@ func (cn *conn) rollback() (err error) {
|
|||||||
_, commandTag, err := cn.simpleExec("ROLLBACK")
|
_, commandTag, err := cn.simpleExec("ROLLBACK")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if cn.isInTransaction() {
|
if cn.isInTransaction() {
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -630,7 +652,7 @@ func (cn *conn) simpleExec(q string) (res driver.Result, commandTag string, err
|
|||||||
case 'T', 'D':
|
case 'T', 'D':
|
||||||
// ignore any results
|
// ignore any results
|
||||||
default:
|
default:
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
errorf("unknown response for simple query: %q", t)
|
errorf("unknown response for simple query: %q", t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -652,7 +674,7 @@ func (cn *conn) simpleQuery(q string) (res *rows, err error) {
|
|||||||
// the user can close, though, to avoid connections from being
|
// the user can close, though, to avoid connections from being
|
||||||
// leaked. A "rows" with done=true works fine for that purpose.
|
// leaked. A "rows" with done=true works fine for that purpose.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
errorf("unexpected message %q in simple query execution", t)
|
errorf("unexpected message %q in simple query execution", t)
|
||||||
}
|
}
|
||||||
if res == nil {
|
if res == nil {
|
||||||
@ -663,8 +685,11 @@ func (cn *conn) simpleQuery(q string) (res *rows, err error) {
|
|||||||
// Set the result and tag to the last command complete if there wasn't a
|
// Set the result and tag to the last command complete if there wasn't a
|
||||||
// query already run. Although queries usually return from here and cede
|
// query already run. Although queries usually return from here and cede
|
||||||
// control to Next, a query with zero results does not.
|
// control to Next, a query with zero results does not.
|
||||||
if t == 'C' && res.colNames == nil {
|
if t == 'C' {
|
||||||
res.result, res.tag = cn.parseComplete(r.string())
|
res.result, res.tag = cn.parseComplete(r.string())
|
||||||
|
if res.colNames != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
res.done = true
|
res.done = true
|
||||||
case 'Z':
|
case 'Z':
|
||||||
@ -676,7 +701,7 @@ func (cn *conn) simpleQuery(q string) (res *rows, err error) {
|
|||||||
err = parseError(r)
|
err = parseError(r)
|
||||||
case 'D':
|
case 'D':
|
||||||
if res == nil {
|
if res == nil {
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
errorf("unexpected DataRow in simple query execution")
|
errorf("unexpected DataRow in simple query execution")
|
||||||
}
|
}
|
||||||
// the query didn't fail; kick off to Next
|
// the query didn't fail; kick off to Next
|
||||||
@ -691,7 +716,7 @@ func (cn *conn) simpleQuery(q string) (res *rows, err error) {
|
|||||||
// To work around a bug in QueryRow in Go 1.2 and earlier, wait
|
// To work around a bug in QueryRow in Go 1.2 and earlier, wait
|
||||||
// until the first DataRow has been received.
|
// until the first DataRow has been received.
|
||||||
default:
|
default:
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
errorf("unknown response for simple query: %q", t)
|
errorf("unknown response for simple query: %q", t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -784,7 +809,7 @@ func (cn *conn) prepareTo(q, stmtName string) *stmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cn *conn) Prepare(q string) (_ driver.Stmt, err error) {
|
func (cn *conn) Prepare(q string) (_ driver.Stmt, err error) {
|
||||||
if cn.bad {
|
if cn.getBad() {
|
||||||
return nil, driver.ErrBadConn
|
return nil, driver.ErrBadConn
|
||||||
}
|
}
|
||||||
defer cn.errRecover(&err)
|
defer cn.errRecover(&err)
|
||||||
@ -823,7 +848,7 @@ func (cn *conn) Query(query string, args []driver.Value) (driver.Rows, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cn *conn) query(query string, args []driver.Value) (_ *rows, err error) {
|
func (cn *conn) query(query string, args []driver.Value) (_ *rows, err error) {
|
||||||
if cn.bad {
|
if cn.getBad() {
|
||||||
return nil, driver.ErrBadConn
|
return nil, driver.ErrBadConn
|
||||||
}
|
}
|
||||||
if cn.inCopy {
|
if cn.inCopy {
|
||||||
@ -857,7 +882,7 @@ func (cn *conn) query(query string, args []driver.Value) (_ *rows, err error) {
|
|||||||
|
|
||||||
// Implement the optional "Execer" interface for one-shot queries
|
// Implement the optional "Execer" interface for one-shot queries
|
||||||
func (cn *conn) Exec(query string, args []driver.Value) (res driver.Result, err error) {
|
func (cn *conn) Exec(query string, args []driver.Value) (res driver.Result, err error) {
|
||||||
if cn.bad {
|
if cn.getBad() {
|
||||||
return nil, driver.ErrBadConn
|
return nil, driver.ErrBadConn
|
||||||
}
|
}
|
||||||
defer cn.errRecover(&err)
|
defer cn.errRecover(&err)
|
||||||
@ -891,9 +916,20 @@ func (cn *conn) Exec(query string, args []driver.Value) (res driver.Result, err
|
|||||||
return r, err
|
return r, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type safeRetryError struct {
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (se *safeRetryError) Error() string {
|
||||||
|
return se.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
func (cn *conn) send(m *writeBuf) {
|
func (cn *conn) send(m *writeBuf) {
|
||||||
_, err := cn.c.Write(m.wrap())
|
n, err := cn.c.Write(m.wrap())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if n == 0 {
|
||||||
|
err = &safeRetryError{Err: err}
|
||||||
|
}
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -918,7 +954,7 @@ func (cn *conn) sendSimpleMessage(typ byte) (err error) {
|
|||||||
// the message yourself.
|
// the message yourself.
|
||||||
func (cn *conn) saveMessage(typ byte, buf *readBuf) {
|
func (cn *conn) saveMessage(typ byte, buf *readBuf) {
|
||||||
if cn.saveMessageType != 0 {
|
if cn.saveMessageType != 0 {
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
errorf("unexpected saveMessageType %d", cn.saveMessageType)
|
errorf("unexpected saveMessageType %d", cn.saveMessageType)
|
||||||
}
|
}
|
||||||
cn.saveMessageType = typ
|
cn.saveMessageType = typ
|
||||||
@ -1288,7 +1324,7 @@ func (st *stmt) Close() (err error) {
|
|||||||
if st.closed {
|
if st.closed {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if st.cn.bad {
|
if st.cn.getBad() {
|
||||||
return driver.ErrBadConn
|
return driver.ErrBadConn
|
||||||
}
|
}
|
||||||
defer st.cn.errRecover(&err)
|
defer st.cn.errRecover(&err)
|
||||||
@ -1302,14 +1338,14 @@ func (st *stmt) Close() (err error) {
|
|||||||
|
|
||||||
t, _ := st.cn.recv1()
|
t, _ := st.cn.recv1()
|
||||||
if t != '3' {
|
if t != '3' {
|
||||||
st.cn.bad = true
|
st.cn.setBad()
|
||||||
errorf("unexpected close response: %q", t)
|
errorf("unexpected close response: %q", t)
|
||||||
}
|
}
|
||||||
st.closed = true
|
st.closed = true
|
||||||
|
|
||||||
t, r := st.cn.recv1()
|
t, r := st.cn.recv1()
|
||||||
if t != 'Z' {
|
if t != 'Z' {
|
||||||
st.cn.bad = true
|
st.cn.setBad()
|
||||||
errorf("expected ready for query, but got: %q", t)
|
errorf("expected ready for query, but got: %q", t)
|
||||||
}
|
}
|
||||||
st.cn.processReadyForQuery(r)
|
st.cn.processReadyForQuery(r)
|
||||||
@ -1318,7 +1354,7 @@ func (st *stmt) Close() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (st *stmt) Query(v []driver.Value) (r driver.Rows, err error) {
|
func (st *stmt) Query(v []driver.Value) (r driver.Rows, err error) {
|
||||||
if st.cn.bad {
|
if st.cn.getBad() {
|
||||||
return nil, driver.ErrBadConn
|
return nil, driver.ErrBadConn
|
||||||
}
|
}
|
||||||
defer st.cn.errRecover(&err)
|
defer st.cn.errRecover(&err)
|
||||||
@ -1331,7 +1367,7 @@ func (st *stmt) Query(v []driver.Value) (r driver.Rows, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (st *stmt) Exec(v []driver.Value) (res driver.Result, err error) {
|
func (st *stmt) Exec(v []driver.Value) (res driver.Result, err error) {
|
||||||
if st.cn.bad {
|
if st.cn.getBad() {
|
||||||
return nil, driver.ErrBadConn
|
return nil, driver.ErrBadConn
|
||||||
}
|
}
|
||||||
defer st.cn.errRecover(&err)
|
defer st.cn.errRecover(&err)
|
||||||
@ -1418,7 +1454,7 @@ func (cn *conn) parseComplete(commandTag string) (driver.Result, string) {
|
|||||||
if affectedRows == nil && strings.HasPrefix(commandTag, "INSERT ") {
|
if affectedRows == nil && strings.HasPrefix(commandTag, "INSERT ") {
|
||||||
parts := strings.Split(commandTag, " ")
|
parts := strings.Split(commandTag, " ")
|
||||||
if len(parts) != 3 {
|
if len(parts) != 3 {
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
errorf("unexpected INSERT command tag %s", commandTag)
|
errorf("unexpected INSERT command tag %s", commandTag)
|
||||||
}
|
}
|
||||||
affectedRows = &parts[len(parts)-1]
|
affectedRows = &parts[len(parts)-1]
|
||||||
@ -1430,7 +1466,7 @@ func (cn *conn) parseComplete(commandTag string) (driver.Result, string) {
|
|||||||
}
|
}
|
||||||
n, err := strconv.ParseInt(*affectedRows, 10, 64)
|
n, err := strconv.ParseInt(*affectedRows, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
errorf("could not parse commandTag: %s", err)
|
errorf("could not parse commandTag: %s", err)
|
||||||
}
|
}
|
||||||
return driver.RowsAffected(n), commandTag
|
return driver.RowsAffected(n), commandTag
|
||||||
@ -1497,7 +1533,7 @@ func (rs *rows) Next(dest []driver.Value) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
conn := rs.cn
|
conn := rs.cn
|
||||||
if conn.bad {
|
if conn.getBad() {
|
||||||
return driver.ErrBadConn
|
return driver.ErrBadConn
|
||||||
}
|
}
|
||||||
defer conn.errRecover(&err)
|
defer conn.errRecover(&err)
|
||||||
@ -1522,7 +1558,7 @@ func (rs *rows) Next(dest []driver.Value) (err error) {
|
|||||||
case 'D':
|
case 'D':
|
||||||
n := rs.rb.int16()
|
n := rs.rb.int16()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conn.bad = true
|
conn.setBad()
|
||||||
errorf("unexpected DataRow after error %s", err)
|
errorf("unexpected DataRow after error %s", err)
|
||||||
}
|
}
|
||||||
if n < len(dest) {
|
if n < len(dest) {
|
||||||
@ -1717,7 +1753,7 @@ func (cn *conn) readReadyForQuery() {
|
|||||||
cn.processReadyForQuery(r)
|
cn.processReadyForQuery(r)
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
errorf("unexpected message %q; expected ReadyForQuery", t)
|
errorf("unexpected message %q; expected ReadyForQuery", t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1737,7 +1773,7 @@ func (cn *conn) readParseResponse() {
|
|||||||
cn.readReadyForQuery()
|
cn.readReadyForQuery()
|
||||||
panic(err)
|
panic(err)
|
||||||
default:
|
default:
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
errorf("unexpected Parse response %q", t)
|
errorf("unexpected Parse response %q", t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1762,7 +1798,7 @@ func (cn *conn) readStatementDescribeResponse() (paramTyps []oid.Oid, colNames [
|
|||||||
cn.readReadyForQuery()
|
cn.readReadyForQuery()
|
||||||
panic(err)
|
panic(err)
|
||||||
default:
|
default:
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
errorf("unexpected Describe statement response %q", t)
|
errorf("unexpected Describe statement response %q", t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1780,7 +1816,7 @@ func (cn *conn) readPortalDescribeResponse() rowsHeader {
|
|||||||
cn.readReadyForQuery()
|
cn.readReadyForQuery()
|
||||||
panic(err)
|
panic(err)
|
||||||
default:
|
default:
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
errorf("unexpected Describe response %q", t)
|
errorf("unexpected Describe response %q", t)
|
||||||
}
|
}
|
||||||
panic("not reached")
|
panic("not reached")
|
||||||
@ -1796,7 +1832,7 @@ func (cn *conn) readBindResponse() {
|
|||||||
cn.readReadyForQuery()
|
cn.readReadyForQuery()
|
||||||
panic(err)
|
panic(err)
|
||||||
default:
|
default:
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
errorf("unexpected Bind response %q", t)
|
errorf("unexpected Bind response %q", t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1823,7 +1859,7 @@ func (cn *conn) postExecuteWorkaround() {
|
|||||||
cn.saveMessage(t, r)
|
cn.saveMessage(t, r)
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
errorf("unexpected message during extended query execution: %q", t)
|
errorf("unexpected message during extended query execution: %q", t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1836,7 +1872,7 @@ func (cn *conn) readExecuteResponse(protocolState string) (res driver.Result, co
|
|||||||
switch t {
|
switch t {
|
||||||
case 'C':
|
case 'C':
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
errorf("unexpected CommandComplete after error %s", err)
|
errorf("unexpected CommandComplete after error %s", err)
|
||||||
}
|
}
|
||||||
res, commandTag = cn.parseComplete(r.string())
|
res, commandTag = cn.parseComplete(r.string())
|
||||||
@ -1850,7 +1886,7 @@ func (cn *conn) readExecuteResponse(protocolState string) (res driver.Result, co
|
|||||||
err = parseError(r)
|
err = parseError(r)
|
||||||
case 'T', 'D', 'I':
|
case 'T', 'D', 'I':
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
errorf("unexpected %q after error %s", t, err)
|
errorf("unexpected %q after error %s", t, err)
|
||||||
}
|
}
|
||||||
if t == 'I' {
|
if t == 'I' {
|
||||||
@ -1858,7 +1894,7 @@ func (cn *conn) readExecuteResponse(protocolState string) (res driver.Result, co
|
|||||||
}
|
}
|
||||||
// ignore any results
|
// ignore any results
|
||||||
default:
|
default:
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
errorf("unknown %s response: %q", protocolState, t)
|
errorf("unknown %s response: %q", protocolState, t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
22
vendor/github.com/lib/pq/conn_go18.go
generated
vendored
22
vendor/github.com/lib/pq/conn_go18.go
generated
vendored
@ -7,6 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -89,10 +90,21 @@ func (cn *conn) Ping(ctx context.Context) error {
|
|||||||
|
|
||||||
func (cn *conn) watchCancel(ctx context.Context) func() {
|
func (cn *conn) watchCancel(ctx context.Context) func() {
|
||||||
if done := ctx.Done(); done != nil {
|
if done := ctx.Done(); done != nil {
|
||||||
finished := make(chan struct{})
|
finished := make(chan struct{}, 1)
|
||||||
go func() {
|
go func() {
|
||||||
select {
|
select {
|
||||||
case <-done:
|
case <-done:
|
||||||
|
select {
|
||||||
|
case finished <- struct{}{}:
|
||||||
|
default:
|
||||||
|
// We raced with the finish func, let the next query handle this with the
|
||||||
|
// context.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the connection state to bad so it does not get reused.
|
||||||
|
cn.setBad()
|
||||||
|
|
||||||
// At this point the function level context is canceled,
|
// At this point the function level context is canceled,
|
||||||
// so it must not be used for the additional network
|
// so it must not be used for the additional network
|
||||||
// request to cancel the query.
|
// request to cancel the query.
|
||||||
@ -101,13 +113,14 @@ func (cn *conn) watchCancel(ctx context.Context) func() {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
_ = cn.cancel(ctxCancel)
|
_ = cn.cancel(ctxCancel)
|
||||||
finished <- struct{}{}
|
|
||||||
case <-finished:
|
case <-finished:
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return func() {
|
return func() {
|
||||||
select {
|
select {
|
||||||
case <-finished:
|
case <-finished:
|
||||||
|
cn.setBad()
|
||||||
|
cn.Close()
|
||||||
case finished <- struct{}{}:
|
case finished <- struct{}{}:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,8 +136,11 @@ func (cn *conn) cancel(ctx context.Context) error {
|
|||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
{
|
{
|
||||||
|
bad := &atomic.Value{}
|
||||||
|
bad.Store(false)
|
||||||
can := conn{
|
can := conn{
|
||||||
c: c,
|
c: c,
|
||||||
|
bad: bad,
|
||||||
}
|
}
|
||||||
err = can.ssl(cn.opts)
|
err = can.ssl(cn.opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
6
vendor/github.com/lib/pq/copy.go
generated
vendored
6
vendor/github.com/lib/pq/copy.go
generated
vendored
@ -176,13 +176,13 @@ func (ci *copyin) resploop() {
|
|||||||
|
|
||||||
func (ci *copyin) setBad() {
|
func (ci *copyin) setBad() {
|
||||||
ci.Lock()
|
ci.Lock()
|
||||||
ci.cn.bad = true
|
ci.cn.setBad()
|
||||||
ci.Unlock()
|
ci.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ci *copyin) isBad() bool {
|
func (ci *copyin) isBad() bool {
|
||||||
ci.Lock()
|
ci.Lock()
|
||||||
b := ci.cn.bad
|
b := ci.cn.getBad()
|
||||||
ci.Unlock()
|
ci.Unlock()
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
@ -213,10 +213,10 @@ func (ci *copyin) setResult(result driver.Result) {
|
|||||||
func (ci *copyin) getResult() driver.Result {
|
func (ci *copyin) getResult() driver.Result {
|
||||||
ci.Lock()
|
ci.Lock()
|
||||||
result := ci.Result
|
result := ci.Result
|
||||||
|
ci.Unlock()
|
||||||
if result == nil {
|
if result == nil {
|
||||||
return driver.RowsAffected(0)
|
return driver.RowsAffected(0)
|
||||||
}
|
}
|
||||||
ci.Unlock()
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
11
vendor/github.com/lib/pq/error.go
generated
vendored
11
vendor/github.com/lib/pq/error.go
generated
vendored
@ -484,7 +484,7 @@ func (cn *conn) errRecover(err *error) {
|
|||||||
case nil:
|
case nil:
|
||||||
// Do nothing
|
// Do nothing
|
||||||
case runtime.Error:
|
case runtime.Error:
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
panic(v)
|
panic(v)
|
||||||
case *Error:
|
case *Error:
|
||||||
if v.Fatal() {
|
if v.Fatal() {
|
||||||
@ -493,8 +493,11 @@ func (cn *conn) errRecover(err *error) {
|
|||||||
*err = v
|
*err = v
|
||||||
}
|
}
|
||||||
case *net.OpError:
|
case *net.OpError:
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
*err = v
|
*err = v
|
||||||
|
case *safeRetryError:
|
||||||
|
cn.setBad()
|
||||||
|
*err = driver.ErrBadConn
|
||||||
case error:
|
case error:
|
||||||
if v == io.EOF || v.(error).Error() == "remote error: handshake failure" {
|
if v == io.EOF || v.(error).Error() == "remote error: handshake failure" {
|
||||||
*err = driver.ErrBadConn
|
*err = driver.ErrBadConn
|
||||||
@ -503,13 +506,13 @@ func (cn *conn) errRecover(err *error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
panic(fmt.Sprintf("unknown error: %#v", e))
|
panic(fmt.Sprintf("unknown error: %#v", e))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Any time we return ErrBadConn, we need to remember it since *Tx doesn't
|
// Any time we return ErrBadConn, we need to remember it since *Tx doesn't
|
||||||
// mark the connection bad in database/sql.
|
// mark the connection bad in database/sql.
|
||||||
if *err == driver.ErrBadConn {
|
if *err == driver.ErrBadConn {
|
||||||
cn.bad = true
|
cn.setBad()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
32
vendor/github.com/lib/pq/ssl.go
generated
vendored
32
vendor/github.com/lib/pq/ssl.go
generated
vendored
@ -59,6 +59,9 @@ func ssl(o values) (func(net.Conn) (net.Conn, error), error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This pseudo-parameter is not recognized by the PostgreSQL server, so let's delete it after use.
|
||||||
|
delete(o, "sslinline")
|
||||||
|
|
||||||
// Accept renegotiation requests initiated by the backend.
|
// Accept renegotiation requests initiated by the backend.
|
||||||
//
|
//
|
||||||
// Renegotiation was deprecated then removed from PostgreSQL 9.5, but
|
// Renegotiation was deprecated then removed from PostgreSQL 9.5, but
|
||||||
@ -83,6 +86,19 @@ func ssl(o values) (func(net.Conn) (net.Conn, error), error) {
|
|||||||
// in the user's home directory. The configured files must exist and have
|
// in the user's home directory. The configured files must exist and have
|
||||||
// the correct permissions.
|
// the correct permissions.
|
||||||
func sslClientCertificates(tlsConf *tls.Config, o values) error {
|
func sslClientCertificates(tlsConf *tls.Config, o values) error {
|
||||||
|
sslinline := o["sslinline"]
|
||||||
|
if sslinline == "true" {
|
||||||
|
cert, err := tls.X509KeyPair([]byte(o["sslcert"]), []byte(o["sslkey"]))
|
||||||
|
// Clear out these params, in case they were to be sent to the PostgreSQL server by mistake
|
||||||
|
o["sslcert"] = ""
|
||||||
|
o["sslkey"] = ""
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tlsConf.Certificates = []tls.Certificate{cert}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// user.Current() might fail when cross-compiling. We have to ignore the
|
// user.Current() might fail when cross-compiling. We have to ignore the
|
||||||
// error and continue without home directory defaults, since we wouldn't
|
// error and continue without home directory defaults, since we wouldn't
|
||||||
// know from where to load them.
|
// know from where to load them.
|
||||||
@ -137,9 +153,19 @@ func sslCertificateAuthority(tlsConf *tls.Config, o values) error {
|
|||||||
if sslrootcert := o["sslrootcert"]; len(sslrootcert) > 0 {
|
if sslrootcert := o["sslrootcert"]; len(sslrootcert) > 0 {
|
||||||
tlsConf.RootCAs = x509.NewCertPool()
|
tlsConf.RootCAs = x509.NewCertPool()
|
||||||
|
|
||||||
cert, err := ioutil.ReadFile(sslrootcert)
|
sslinline := o["sslinline"]
|
||||||
if err != nil {
|
|
||||||
return err
|
var cert []byte
|
||||||
|
if sslinline == "true" {
|
||||||
|
// // Clear out this param, in case it were to be sent to the PostgreSQL server by mistake
|
||||||
|
o["sslrootcert"] = ""
|
||||||
|
cert = []byte(sslrootcert)
|
||||||
|
} else {
|
||||||
|
var err error
|
||||||
|
cert, err = ioutil.ReadFile(sslrootcert)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !tlsConf.RootCAs.AppendCertsFromPEM(cert) {
|
if !tlsConf.RootCAs.AppendCertsFromPEM(cert) {
|
||||||
|
4
vendor/github.com/lib/pq/url.go
generated
vendored
4
vendor/github.com/lib/pq/url.go
generated
vendored
@ -40,10 +40,10 @@ func ParseURL(url string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var kvs []string
|
var kvs []string
|
||||||
escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`)
|
escaper := strings.NewReplacer(`'`, `\'`, `\`, `\\`)
|
||||||
accrue := func(k, v string) {
|
accrue := func(k, v string) {
|
||||||
if v != "" {
|
if v != "" {
|
||||||
kvs = append(kvs, k+"="+escaper.Replace(v))
|
kvs = append(kvs, k+"='"+escaper.Replace(v)+"'")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
28
vendor/github.com/mattn/go-colorable/colorable_windows.go
generated
vendored
28
vendor/github.com/mattn/go-colorable/colorable_windows.go
generated
vendored
@ -10,6 +10,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
@ -27,6 +28,7 @@ const (
|
|||||||
backgroundRed = 0x40
|
backgroundRed = 0x40
|
||||||
backgroundIntensity = 0x80
|
backgroundIntensity = 0x80
|
||||||
backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity)
|
backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity)
|
||||||
|
commonLvbUnderscore = 0x8000
|
||||||
|
|
||||||
cENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4
|
cENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4
|
||||||
)
|
)
|
||||||
@ -93,6 +95,7 @@ type Writer struct {
|
|||||||
oldattr word
|
oldattr word
|
||||||
oldpos coord
|
oldpos coord
|
||||||
rest bytes.Buffer
|
rest bytes.Buffer
|
||||||
|
mutex sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewColorable returns new instance of Writer which handles escape sequence from File.
|
// NewColorable returns new instance of Writer which handles escape sequence from File.
|
||||||
@ -432,6 +435,8 @@ func atoiWithDefault(s string, def int) (int, error) {
|
|||||||
|
|
||||||
// Write writes data on console
|
// Write writes data on console
|
||||||
func (w *Writer) Write(data []byte) (n int, err error) {
|
func (w *Writer) Write(data []byte) (n int, err error) {
|
||||||
|
w.mutex.Lock()
|
||||||
|
defer w.mutex.Unlock()
|
||||||
var csbi consoleScreenBufferInfo
|
var csbi consoleScreenBufferInfo
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
|
|
||||||
@ -683,14 +688,19 @@ loop:
|
|||||||
switch {
|
switch {
|
||||||
case n == 0 || n == 100:
|
case n == 0 || n == 100:
|
||||||
attr = w.oldattr
|
attr = w.oldattr
|
||||||
case 1 <= n && n <= 5:
|
case n == 4:
|
||||||
|
attr |= commonLvbUnderscore
|
||||||
|
case (1 <= n && n <= 3) || n == 5:
|
||||||
attr |= foregroundIntensity
|
attr |= foregroundIntensity
|
||||||
case n == 7:
|
case n == 7 || n == 27:
|
||||||
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
|
attr =
|
||||||
case n == 22 || n == 25:
|
(attr &^ (foregroundMask | backgroundMask)) |
|
||||||
attr |= foregroundIntensity
|
((attr & foregroundMask) << 4) |
|
||||||
case n == 27:
|
((attr & backgroundMask) >> 4)
|
||||||
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
|
case n == 22:
|
||||||
|
attr &^= foregroundIntensity
|
||||||
|
case n == 24:
|
||||||
|
attr &^= commonLvbUnderscore
|
||||||
case 30 <= n && n <= 37:
|
case 30 <= n && n <= 37:
|
||||||
attr &= backgroundMask
|
attr &= backgroundMask
|
||||||
if (n-30)&1 != 0 {
|
if (n-30)&1 != 0 {
|
||||||
@ -709,7 +719,7 @@ loop:
|
|||||||
n256setup()
|
n256setup()
|
||||||
}
|
}
|
||||||
attr &= backgroundMask
|
attr &= backgroundMask
|
||||||
attr |= n256foreAttr[n256]
|
attr |= n256foreAttr[n256%len(n256foreAttr)]
|
||||||
i += 2
|
i += 2
|
||||||
}
|
}
|
||||||
} else if len(token) == 5 && token[i+1] == "2" {
|
} else if len(token) == 5 && token[i+1] == "2" {
|
||||||
@ -751,7 +761,7 @@ loop:
|
|||||||
n256setup()
|
n256setup()
|
||||||
}
|
}
|
||||||
attr &= foregroundMask
|
attr &= foregroundMask
|
||||||
attr |= n256backAttr[n256]
|
attr |= n256backAttr[n256%len(n256backAttr)]
|
||||||
i += 2
|
i += 2
|
||||||
}
|
}
|
||||||
} else if len(token) == 5 && token[i+1] == "2" {
|
} else if len(token) == 5 && token[i+1] == "2" {
|
||||||
|
2
vendor/github.com/valyala/fasttemplate/go.mod
generated
vendored
2
vendor/github.com/valyala/fasttemplate/go.mod
generated
vendored
@ -1,3 +1,5 @@
|
|||||||
module github.com/valyala/fasttemplate
|
module github.com/valyala/fasttemplate
|
||||||
|
|
||||||
|
go 1.12
|
||||||
|
|
||||||
require github.com/valyala/bytebufferpool v1.0.0
|
require github.com/valyala/bytebufferpool v1.0.0
|
||||||
|
132
vendor/github.com/valyala/fasttemplate/template.go
generated
vendored
132
vendor/github.com/valyala/fasttemplate/template.go
generated
vendored
@ -9,8 +9,9 @@ package fasttemplate
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/valyala/bytebufferpool"
|
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"github.com/valyala/bytebufferpool"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExecuteFunc calls f on each template tag (placeholder) occurrence.
|
// ExecuteFunc calls f on each template tag (placeholder) occurrence.
|
||||||
@ -49,6 +50,9 @@ func ExecuteFunc(template, startTag, endTag string, w io.Writer, f TagFunc) (int
|
|||||||
|
|
||||||
ni, err = f(w, unsafeBytes2String(s[:n]))
|
ni, err = f(w, unsafeBytes2String(s[:n]))
|
||||||
nn += int64(ni)
|
nn += int64(ni)
|
||||||
|
if err != nil {
|
||||||
|
return nn, err
|
||||||
|
}
|
||||||
s = s[n+len(b):]
|
s = s[n+len(b):]
|
||||||
}
|
}
|
||||||
ni, err = w.Write(s)
|
ni, err = w.Write(s)
|
||||||
@ -73,6 +77,22 @@ func Execute(template, startTag, endTag string, w io.Writer, m map[string]interf
|
|||||||
return ExecuteFunc(template, startTag, endTag, w, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
|
return ExecuteFunc(template, startTag, endTag, w, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExecuteStd works the same way as Execute, but keeps the unknown placeholders.
|
||||||
|
// This can be used as a drop-in replacement for strings.Replacer
|
||||||
|
//
|
||||||
|
// Substitution map m may contain values with the following types:
|
||||||
|
// * []byte - the fastest value type
|
||||||
|
// * string - convenient value type
|
||||||
|
// * TagFunc - flexible value type
|
||||||
|
//
|
||||||
|
// Returns the number of bytes written to w.
|
||||||
|
//
|
||||||
|
// This function is optimized for constantly changing templates.
|
||||||
|
// Use Template.ExecuteStd for frozen templates.
|
||||||
|
func ExecuteStd(template, startTag, endTag string, w io.Writer, m map[string]interface{}) (int64, error) {
|
||||||
|
return ExecuteFunc(template, startTag, endTag, w, func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, startTag, endTag, tag, m) })
|
||||||
|
}
|
||||||
|
|
||||||
// ExecuteFuncString calls f on each template tag (placeholder) occurrence
|
// ExecuteFuncString calls f on each template tag (placeholder) occurrence
|
||||||
// and substitutes it with the data written to TagFunc's w.
|
// and substitutes it with the data written to TagFunc's w.
|
||||||
//
|
//
|
||||||
@ -81,19 +101,32 @@ func Execute(template, startTag, endTag string, w io.Writer, m map[string]interf
|
|||||||
// This function is optimized for constantly changing templates.
|
// This function is optimized for constantly changing templates.
|
||||||
// Use Template.ExecuteFuncString for frozen templates.
|
// Use Template.ExecuteFuncString for frozen templates.
|
||||||
func ExecuteFuncString(template, startTag, endTag string, f TagFunc) string {
|
func ExecuteFuncString(template, startTag, endTag string, f TagFunc) string {
|
||||||
|
s, err := ExecuteFuncStringWithErr(template, startTag, endTag, f)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("unexpected error: %s", err))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecuteFuncStringWithErr is nearly the same as ExecuteFuncString
|
||||||
|
// but when f returns an error, ExecuteFuncStringWithErr won't panic like ExecuteFuncString
|
||||||
|
// it just returns an empty string and the error f returned
|
||||||
|
func ExecuteFuncStringWithErr(template, startTag, endTag string, f TagFunc) (string, error) {
|
||||||
tagsCount := bytes.Count(unsafeString2Bytes(template), unsafeString2Bytes(startTag))
|
tagsCount := bytes.Count(unsafeString2Bytes(template), unsafeString2Bytes(startTag))
|
||||||
if tagsCount == 0 {
|
if tagsCount == 0 {
|
||||||
return template
|
return template, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
bb := byteBufferPool.Get()
|
bb := byteBufferPool.Get()
|
||||||
if _, err := ExecuteFunc(template, startTag, endTag, bb, f); err != nil {
|
if _, err := ExecuteFunc(template, startTag, endTag, bb, f); err != nil {
|
||||||
panic(fmt.Sprintf("unexpected error: %s", err))
|
bb.Reset()
|
||||||
|
byteBufferPool.Put(bb)
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
s := string(bb.B)
|
s := string(bb.B)
|
||||||
bb.Reset()
|
bb.Reset()
|
||||||
byteBufferPool.Put(bb)
|
byteBufferPool.Put(bb)
|
||||||
return s
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var byteBufferPool bytebufferpool.Pool
|
var byteBufferPool bytebufferpool.Pool
|
||||||
@ -112,6 +145,20 @@ func ExecuteString(template, startTag, endTag string, m map[string]interface{})
|
|||||||
return ExecuteFuncString(template, startTag, endTag, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
|
return ExecuteFuncString(template, startTag, endTag, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExecuteStringStd works the same way as ExecuteString, but keeps the unknown placeholders.
|
||||||
|
// This can be used as a drop-in replacement for strings.Replacer
|
||||||
|
//
|
||||||
|
// Substitution map m may contain values with the following types:
|
||||||
|
// * []byte - the fastest value type
|
||||||
|
// * string - convenient value type
|
||||||
|
// * TagFunc - flexible value type
|
||||||
|
//
|
||||||
|
// This function is optimized for constantly changing templates.
|
||||||
|
// Use Template.ExecuteStringStd for frozen templates.
|
||||||
|
func ExecuteStringStd(template, startTag, endTag string, m map[string]interface{}) string {
|
||||||
|
return ExecuteFuncString(template, startTag, endTag, func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, startTag, endTag, tag, m) })
|
||||||
|
}
|
||||||
|
|
||||||
// Template implements simple template engine, which can be used for fast
|
// Template implements simple template engine, which can be used for fast
|
||||||
// tags' (aka placeholders) substitution.
|
// tags' (aka placeholders) substitution.
|
||||||
type Template struct {
|
type Template struct {
|
||||||
@ -267,6 +314,19 @@ func (t *Template) Execute(w io.Writer, m map[string]interface{}) (int64, error)
|
|||||||
return t.ExecuteFunc(w, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
|
return t.ExecuteFunc(w, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExecuteStd works the same way as Execute, but keeps the unknown placeholders.
|
||||||
|
// This can be used as a drop-in replacement for strings.Replacer
|
||||||
|
//
|
||||||
|
// Substitution map m may contain values with the following types:
|
||||||
|
// * []byte - the fastest value type
|
||||||
|
// * string - convenient value type
|
||||||
|
// * TagFunc - flexible value type
|
||||||
|
//
|
||||||
|
// Returns the number of bytes written to w.
|
||||||
|
func (t *Template) ExecuteStd(w io.Writer, m map[string]interface{}) (int64, error) {
|
||||||
|
return t.ExecuteFunc(w, func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, t.startTag, t.endTag, tag, m) })
|
||||||
|
}
|
||||||
|
|
||||||
// ExecuteFuncString calls f on each template tag (placeholder) occurrence
|
// ExecuteFuncString calls f on each template tag (placeholder) occurrence
|
||||||
// and substitutes it with the data written to TagFunc's w.
|
// and substitutes it with the data written to TagFunc's w.
|
||||||
//
|
//
|
||||||
@ -275,14 +335,31 @@ func (t *Template) Execute(w io.Writer, m map[string]interface{}) (int64, error)
|
|||||||
// This function is optimized for frozen templates.
|
// This function is optimized for frozen templates.
|
||||||
// Use ExecuteFuncString for constantly changing templates.
|
// Use ExecuteFuncString for constantly changing templates.
|
||||||
func (t *Template) ExecuteFuncString(f TagFunc) string {
|
func (t *Template) ExecuteFuncString(f TagFunc) string {
|
||||||
|
s, err := t.ExecuteFuncStringWithErr(f)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("unexpected error: %s", err))
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecuteFuncStringWithErr calls f on each template tag (placeholder) occurrence
|
||||||
|
// and substitutes it with the data written to TagFunc's w.
|
||||||
|
//
|
||||||
|
// Returns the resulting string.
|
||||||
|
//
|
||||||
|
// This function is optimized for frozen templates.
|
||||||
|
// Use ExecuteFuncString for constantly changing templates.
|
||||||
|
func (t *Template) ExecuteFuncStringWithErr(f TagFunc) (string, error) {
|
||||||
bb := t.byteBufferPool.Get()
|
bb := t.byteBufferPool.Get()
|
||||||
if _, err := t.ExecuteFunc(bb, f); err != nil {
|
if _, err := t.ExecuteFunc(bb, f); err != nil {
|
||||||
panic(fmt.Sprintf("unexpected error: %s", err))
|
bb.Reset()
|
||||||
|
t.byteBufferPool.Put(bb)
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
s := string(bb.Bytes())
|
s := string(bb.Bytes())
|
||||||
bb.Reset()
|
bb.Reset()
|
||||||
t.byteBufferPool.Put(bb)
|
t.byteBufferPool.Put(bb)
|
||||||
return s
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecuteString substitutes template tags (placeholders) with the corresponding
|
// ExecuteString substitutes template tags (placeholders) with the corresponding
|
||||||
@ -299,6 +376,20 @@ func (t *Template) ExecuteString(m map[string]interface{}) string {
|
|||||||
return t.ExecuteFuncString(func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
|
return t.ExecuteFuncString(func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExecuteStringStd works the same way as ExecuteString, but keeps the unknown placeholders.
|
||||||
|
// This can be used as a drop-in replacement for strings.Replacer
|
||||||
|
//
|
||||||
|
// Substitution map m may contain values with the following types:
|
||||||
|
// * []byte - the fastest value type
|
||||||
|
// * string - convenient value type
|
||||||
|
// * TagFunc - flexible value type
|
||||||
|
//
|
||||||
|
// This function is optimized for frozen templates.
|
||||||
|
// Use ExecuteStringStd for constantly changing templates.
|
||||||
|
func (t *Template) ExecuteStringStd(m map[string]interface{}) string {
|
||||||
|
return t.ExecuteFuncString(func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, t.startTag, t.endTag, tag, m) })
|
||||||
|
}
|
||||||
|
|
||||||
func stdTagFunc(w io.Writer, tag string, m map[string]interface{}) (int, error) {
|
func stdTagFunc(w io.Writer, tag string, m map[string]interface{}) (int, error) {
|
||||||
v := m[tag]
|
v := m[tag]
|
||||||
if v == nil {
|
if v == nil {
|
||||||
@ -315,3 +406,32 @@ func stdTagFunc(w io.Writer, tag string, m map[string]interface{}) (int, error)
|
|||||||
panic(fmt.Sprintf("tag=%q contains unexpected value type=%#v. Expected []byte, string or TagFunc", tag, v))
|
panic(fmt.Sprintf("tag=%q contains unexpected value type=%#v. Expected []byte, string or TagFunc", tag, v))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func keepUnknownTagFunc(w io.Writer, startTag, endTag, tag string, m map[string]interface{}) (int, error) {
|
||||||
|
v, ok := m[tag]
|
||||||
|
if !ok {
|
||||||
|
if _, err := w.Write(unsafeString2Bytes(startTag)); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if _, err := w.Write(unsafeString2Bytes(tag)); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if _, err := w.Write(unsafeString2Bytes(endTag)); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return len(startTag) + len(tag) + len(endTag), nil
|
||||||
|
}
|
||||||
|
if v == nil {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
switch value := v.(type) {
|
||||||
|
case []byte:
|
||||||
|
return w.Write(value)
|
||||||
|
case string:
|
||||||
|
return w.Write([]byte(value))
|
||||||
|
case TagFunc:
|
||||||
|
return value(w, tag)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("tag=%q contains unexpected value type=%#v. Expected []byte, string or TagFunc", tag, v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
13
vendor/github.com/valyala/fasttemplate/unsafe.go
generated
vendored
13
vendor/github.com/valyala/fasttemplate/unsafe.go
generated
vendored
@ -11,12 +11,11 @@ func unsafeBytes2String(b []byte) string {
|
|||||||
return *(*string)(unsafe.Pointer(&b))
|
return *(*string)(unsafe.Pointer(&b))
|
||||||
}
|
}
|
||||||
|
|
||||||
func unsafeString2Bytes(s string) []byte {
|
func unsafeString2Bytes(s string) (b []byte) {
|
||||||
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
|
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
|
||||||
bh := reflect.SliceHeader{
|
bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||||
Data: sh.Data,
|
bh.Data = sh.Data
|
||||||
Len: sh.Len,
|
bh.Cap = sh.Len
|
||||||
Cap: sh.Len,
|
bh.Len = sh.Len
|
||||||
}
|
return b
|
||||||
return *(*[]byte)(unsafe.Pointer(&bh))
|
|
||||||
}
|
}
|
||||||
|
4
vendor/golang.org/x/crypto/acme/acme.go
generated
vendored
4
vendor/golang.org/x/crypto/acme/acme.go
generated
vendored
@ -363,6 +363,10 @@ func AcceptTOS(tosURL string) bool { return true }
|
|||||||
// Also see Error's Instance field for when a CA requires already registered accounts to agree
|
// Also see Error's Instance field for when a CA requires already registered accounts to agree
|
||||||
// to an updated Terms of Service.
|
// to an updated Terms of Service.
|
||||||
func (c *Client) Register(ctx context.Context, acct *Account, prompt func(tosURL string) bool) (*Account, error) {
|
func (c *Client) Register(ctx context.Context, acct *Account, prompt func(tosURL string) bool) (*Account, error) {
|
||||||
|
if c.Key == nil {
|
||||||
|
return nil, errors.New("acme: client.Key must be set to Register")
|
||||||
|
}
|
||||||
|
|
||||||
dir, err := c.Discover(ctx)
|
dir, err := c.Discover(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
8
vendor/golang.org/x/crypto/acme/autocert/autocert.go
generated
vendored
8
vendor/golang.org/x/crypto/acme/autocert/autocert.go
generated
vendored
@ -1133,11 +1133,11 @@ func (s *certState) tlscert() (*tls.Certificate, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// certRequest generates a CSR for the given common name cn and optional SANs.
|
// certRequest generates a CSR for the given common name.
|
||||||
func certRequest(key crypto.Signer, cn string, ext []pkix.Extension, san ...string) ([]byte, error) {
|
func certRequest(key crypto.Signer, name string, ext []pkix.Extension) ([]byte, error) {
|
||||||
req := &x509.CertificateRequest{
|
req := &x509.CertificateRequest{
|
||||||
Subject: pkix.Name{CommonName: cn},
|
Subject: pkix.Name{CommonName: name},
|
||||||
DNSNames: san,
|
DNSNames: []string{name},
|
||||||
ExtraExtensions: ext,
|
ExtraExtensions: ext,
|
||||||
}
|
}
|
||||||
return x509.CreateCertificateRequest(rand.Reader, req, key)
|
return x509.CreateCertificateRequest(rand.Reader, req, key)
|
||||||
|
54
vendor/golang.org/x/crypto/acme/jws.go
generated
vendored
54
vendor/golang.org/x/crypto/acme/jws.go
generated
vendored
@ -7,6 +7,7 @@ package acme
|
|||||||
import (
|
import (
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"crypto/hmac"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
@ -14,6 +15,7 @@ import (
|
|||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
)
|
)
|
||||||
@ -31,6 +33,14 @@ const noKeyID = keyID("")
|
|||||||
// See https://tools.ietf.org/html/rfc8555#section-6.3 for more details.
|
// See https://tools.ietf.org/html/rfc8555#section-6.3 for more details.
|
||||||
const noPayload = ""
|
const noPayload = ""
|
||||||
|
|
||||||
|
// jsonWebSignature can be easily serialized into a JWS following
|
||||||
|
// https://tools.ietf.org/html/rfc7515#section-3.2.
|
||||||
|
type jsonWebSignature struct {
|
||||||
|
Protected string `json:"protected"`
|
||||||
|
Payload string `json:"payload"`
|
||||||
|
Sig string `json:"signature"`
|
||||||
|
}
|
||||||
|
|
||||||
// jwsEncodeJSON signs claimset using provided key and a nonce.
|
// jwsEncodeJSON signs claimset using provided key and a nonce.
|
||||||
// The result is serialized in JSON format containing either kid or jwk
|
// The result is serialized in JSON format containing either kid or jwk
|
||||||
// fields based on the provided keyID value.
|
// fields based on the provided keyID value.
|
||||||
@ -71,12 +81,7 @@ func jwsEncodeJSON(claimset interface{}, key crypto.Signer, kid keyID, nonce, ur
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
enc := jsonWebSignature{
|
||||||
enc := struct {
|
|
||||||
Protected string `json:"protected"`
|
|
||||||
Payload string `json:"payload"`
|
|
||||||
Sig string `json:"signature"`
|
|
||||||
}{
|
|
||||||
Protected: phead,
|
Protected: phead,
|
||||||
Payload: payload,
|
Payload: payload,
|
||||||
Sig: base64.RawURLEncoding.EncodeToString(sig),
|
Sig: base64.RawURLEncoding.EncodeToString(sig),
|
||||||
@ -84,6 +89,43 @@ func jwsEncodeJSON(claimset interface{}, key crypto.Signer, kid keyID, nonce, ur
|
|||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// jwsWithMAC creates and signs a JWS using the given key and the HS256
|
||||||
|
// algorithm. kid and url are included in the protected header. rawPayload
|
||||||
|
// should not be base64-URL-encoded.
|
||||||
|
func jwsWithMAC(key []byte, kid, url string, rawPayload []byte) (*jsonWebSignature, error) {
|
||||||
|
if len(key) == 0 {
|
||||||
|
return nil, errors.New("acme: cannot sign JWS with an empty MAC key")
|
||||||
|
}
|
||||||
|
header := struct {
|
||||||
|
Algorithm string `json:"alg"`
|
||||||
|
KID string `json:"kid"`
|
||||||
|
URL string `json:"url,omitempty"`
|
||||||
|
}{
|
||||||
|
// Only HMAC-SHA256 is supported.
|
||||||
|
Algorithm: "HS256",
|
||||||
|
KID: kid,
|
||||||
|
URL: url,
|
||||||
|
}
|
||||||
|
rawProtected, err := json.Marshal(header)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
protected := base64.RawURLEncoding.EncodeToString(rawProtected)
|
||||||
|
payload := base64.RawURLEncoding.EncodeToString(rawPayload)
|
||||||
|
|
||||||
|
h := hmac.New(sha256.New, key)
|
||||||
|
if _, err := h.Write([]byte(protected + "." + payload)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mac := h.Sum(nil)
|
||||||
|
|
||||||
|
return &jsonWebSignature{
|
||||||
|
Protected: protected,
|
||||||
|
Payload: payload,
|
||||||
|
Sig: base64.RawURLEncoding.EncodeToString(mac),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// jwkEncode encodes public part of an RSA or ECDSA key into a JWK.
|
// jwkEncode encodes public part of an RSA or ECDSA key into a JWK.
|
||||||
// The result is also suitable for creating a JWK thumbprint.
|
// The result is also suitable for creating a JWK thumbprint.
|
||||||
// https://tools.ietf.org/html/rfc7517
|
// https://tools.ietf.org/html/rfc7517
|
||||||
|
30
vendor/golang.org/x/crypto/acme/rfc8555.go
generated
vendored
30
vendor/golang.org/x/crypto/acme/rfc8555.go
generated
vendored
@ -37,22 +37,32 @@ func (c *Client) DeactivateReg(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// registerRFC is quivalent to c.Register but for CAs implementing RFC 8555.
|
// registerRFC is equivalent to c.Register but for CAs implementing RFC 8555.
|
||||||
// It expects c.Discover to have already been called.
|
// It expects c.Discover to have already been called.
|
||||||
// TODO: Implement externalAccountBinding.
|
|
||||||
func (c *Client) registerRFC(ctx context.Context, acct *Account, prompt func(tosURL string) bool) (*Account, error) {
|
func (c *Client) registerRFC(ctx context.Context, acct *Account, prompt func(tosURL string) bool) (*Account, error) {
|
||||||
c.cacheMu.Lock() // guard c.kid access
|
c.cacheMu.Lock() // guard c.kid access
|
||||||
defer c.cacheMu.Unlock()
|
defer c.cacheMu.Unlock()
|
||||||
|
|
||||||
req := struct {
|
req := struct {
|
||||||
TermsAgreed bool `json:"termsOfServiceAgreed,omitempty"`
|
TermsAgreed bool `json:"termsOfServiceAgreed,omitempty"`
|
||||||
Contact []string `json:"contact,omitempty"`
|
Contact []string `json:"contact,omitempty"`
|
||||||
|
ExternalAccountBinding *jsonWebSignature `json:"externalAccountBinding,omitempty"`
|
||||||
}{
|
}{
|
||||||
Contact: acct.Contact,
|
Contact: acct.Contact,
|
||||||
}
|
}
|
||||||
if c.dir.Terms != "" {
|
if c.dir.Terms != "" {
|
||||||
req.TermsAgreed = prompt(c.dir.Terms)
|
req.TermsAgreed = prompt(c.dir.Terms)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set 'externalAccountBinding' field if requested
|
||||||
|
if acct.ExternalAccountBinding != nil {
|
||||||
|
eabJWS, err := c.encodeExternalAccountBinding(acct.ExternalAccountBinding)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("acme: failed to encode external account binding: %v", err)
|
||||||
|
}
|
||||||
|
req.ExternalAccountBinding = eabJWS
|
||||||
|
}
|
||||||
|
|
||||||
res, err := c.post(ctx, c.Key, c.dir.RegURL, req, wantStatus(
|
res, err := c.post(ctx, c.Key, c.dir.RegURL, req, wantStatus(
|
||||||
http.StatusOK, // account with this key already registered
|
http.StatusOK, // account with this key already registered
|
||||||
http.StatusCreated, // new account created
|
http.StatusCreated, // new account created
|
||||||
@ -75,7 +85,17 @@ func (c *Client) registerRFC(ctx context.Context, acct *Account, prompt func(tos
|
|||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateGegRFC is equivalent to c.UpdateReg but for CAs implementing RFC 8555.
|
// encodeExternalAccountBinding will encode an external account binding stanza
|
||||||
|
// as described in https://tools.ietf.org/html/rfc8555#section-7.3.4.
|
||||||
|
func (c *Client) encodeExternalAccountBinding(eab *ExternalAccountBinding) (*jsonWebSignature, error) {
|
||||||
|
jwk, err := jwkEncode(c.Key.Public())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return jwsWithMAC(eab.Key, eab.KID, c.dir.RegURL, []byte(jwk))
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateRegRFC is equivalent to c.UpdateReg but for CAs implementing RFC 8555.
|
||||||
// It expects c.Discover to have already been called.
|
// It expects c.Discover to have already been called.
|
||||||
func (c *Client) updateRegRFC(ctx context.Context, a *Account) (*Account, error) {
|
func (c *Client) updateRegRFC(ctx context.Context, a *Account) (*Account, error) {
|
||||||
url := string(c.accountKID(ctx))
|
url := string(c.accountKID(ctx))
|
||||||
|
36
vendor/golang.org/x/crypto/acme/types.go
generated
vendored
36
vendor/golang.org/x/crypto/acme/types.go
generated
vendored
@ -102,7 +102,12 @@ func (a *AuthorizationError) Error() string {
|
|||||||
for i, err := range a.Errors {
|
for i, err := range a.Errors {
|
||||||
e[i] = err.Error()
|
e[i] = err.Error()
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("acme: authorization error for %s: %s", a.Identifier, strings.Join(e, "; "))
|
|
||||||
|
if a.Identifier != "" {
|
||||||
|
return fmt.Sprintf("acme: authorization error for %s: %s", a.Identifier, strings.Join(e, "; "))
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("acme: authorization error: %s", strings.Join(e, "; "))
|
||||||
}
|
}
|
||||||
|
|
||||||
// OrderError is returned from Client's order related methods.
|
// OrderError is returned from Client's order related methods.
|
||||||
@ -194,6 +199,28 @@ type Account struct {
|
|||||||
//
|
//
|
||||||
// It is non-RFC 8555 compliant and is obsoleted by OrdersURL.
|
// It is non-RFC 8555 compliant and is obsoleted by OrdersURL.
|
||||||
Certificates string
|
Certificates string
|
||||||
|
|
||||||
|
// ExternalAccountBinding represents an arbitrary binding to an account of
|
||||||
|
// the CA which the ACME server is tied to.
|
||||||
|
// See https://tools.ietf.org/html/rfc8555#section-7.3.4 for more details.
|
||||||
|
ExternalAccountBinding *ExternalAccountBinding
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExternalAccountBinding contains the data needed to form a request with
|
||||||
|
// an external account binding.
|
||||||
|
// See https://tools.ietf.org/html/rfc8555#section-7.3.4 for more details.
|
||||||
|
type ExternalAccountBinding struct {
|
||||||
|
// KID is the Key ID of the symmetric MAC key that the CA provides to
|
||||||
|
// identify an external account from ACME.
|
||||||
|
KID string
|
||||||
|
|
||||||
|
// Key is the bytes of the symmetric key that the CA provides to identify
|
||||||
|
// the account. Key must correspond to the KID.
|
||||||
|
Key []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ExternalAccountBinding) String() string {
|
||||||
|
return fmt.Sprintf("&{KID: %q, Key: redacted}", e.KID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Directory is ACME server discovery data.
|
// Directory is ACME server discovery data.
|
||||||
@ -407,6 +434,7 @@ type wireAuthz struct {
|
|||||||
Wildcard bool
|
Wildcard bool
|
||||||
Challenges []wireChallenge
|
Challenges []wireChallenge
|
||||||
Combinations [][]int
|
Combinations [][]int
|
||||||
|
Error *wireError
|
||||||
}
|
}
|
||||||
|
|
||||||
func (z *wireAuthz) authorization(uri string) *Authorization {
|
func (z *wireAuthz) authorization(uri string) *Authorization {
|
||||||
@ -430,11 +458,17 @@ func (z *wireAuthz) error(uri string) *AuthorizationError {
|
|||||||
URI: uri,
|
URI: uri,
|
||||||
Identifier: z.Identifier.Value,
|
Identifier: z.Identifier.Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if z.Error != nil {
|
||||||
|
err.Errors = append(err.Errors, z.Error.error(nil))
|
||||||
|
}
|
||||||
|
|
||||||
for _, raw := range z.Challenges {
|
for _, raw := range z.Challenges {
|
||||||
if raw.Error != nil {
|
if raw.Error != nil {
|
||||||
err.Errors = append(err.Errors, raw.Error.error(nil))
|
err.Errors = append(err.Errors, raw.Error.error(nil))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
vendor/golang.org/x/crypto/acme/version_go112.go
generated
vendored
1
vendor/golang.org/x/crypto/acme/version_go112.go
generated
vendored
@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build go1.12
|
||||||
// +build go1.12
|
// +build go1.12
|
||||||
|
|
||||||
package acme
|
package acme
|
||||||
|
8
vendor/golang.org/x/net/http2/client_conn_pool.go
generated
vendored
8
vendor/golang.org/x/net/http2/client_conn_pool.go
generated
vendored
@ -107,6 +107,7 @@ func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMis
|
|||||||
|
|
||||||
// dialCall is an in-flight Transport dial call to a host.
|
// dialCall is an in-flight Transport dial call to a host.
|
||||||
type dialCall struct {
|
type dialCall struct {
|
||||||
|
_ incomparable
|
||||||
p *clientConnPool
|
p *clientConnPool
|
||||||
done chan struct{} // closed when done
|
done chan struct{} // closed when done
|
||||||
res *ClientConn // valid after done is closed
|
res *ClientConn // valid after done is closed
|
||||||
@ -180,6 +181,7 @@ func (p *clientConnPool) addConnIfNeeded(key string, t *Transport, c *tls.Conn)
|
|||||||
}
|
}
|
||||||
|
|
||||||
type addConnCall struct {
|
type addConnCall struct {
|
||||||
|
_ incomparable
|
||||||
p *clientConnPool
|
p *clientConnPool
|
||||||
done chan struct{} // closed when done
|
done chan struct{} // closed when done
|
||||||
err error
|
err error
|
||||||
@ -200,12 +202,6 @@ func (c *addConnCall) run(t *Transport, key string, tc *tls.Conn) {
|
|||||||
close(c.done)
|
close(c.done)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *clientConnPool) addConn(key string, cc *ClientConn) {
|
|
||||||
p.mu.Lock()
|
|
||||||
p.addConnLocked(key, cc)
|
|
||||||
p.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// p.mu must be held
|
// p.mu must be held
|
||||||
func (p *clientConnPool) addConnLocked(key string, cc *ClientConn) {
|
func (p *clientConnPool) addConnLocked(key string, cc *ClientConn) {
|
||||||
for _, v := range p.conns[key] {
|
for _, v := range p.conns[key] {
|
||||||
|
2
vendor/golang.org/x/net/http2/flow.go
generated
vendored
2
vendor/golang.org/x/net/http2/flow.go
generated
vendored
@ -8,6 +8,8 @@ package http2
|
|||||||
|
|
||||||
// flow is the flow control window's size.
|
// flow is the flow control window's size.
|
||||||
type flow struct {
|
type flow struct {
|
||||||
|
_ incomparable
|
||||||
|
|
||||||
// n is the number of DATA bytes we're allowed to send.
|
// n is the number of DATA bytes we're allowed to send.
|
||||||
// A flow is kept both on a conn and a per-stream.
|
// A flow is kept both on a conn and a per-stream.
|
||||||
n int32
|
n int32
|
||||||
|
1
vendor/golang.org/x/net/http2/go111.go
generated
vendored
1
vendor/golang.org/x/net/http2/go111.go
generated
vendored
@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build go1.11
|
||||||
// +build go1.11
|
// +build go1.11
|
||||||
|
|
||||||
package http2
|
package http2
|
||||||
|
10
vendor/golang.org/x/net/http2/h2c/h2c.go
generated
vendored
10
vendor/golang.org/x/net/http2/h2c/h2c.go
generated
vendored
@ -84,14 +84,20 @@ func (s h2cHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
s.s.ServeConn(conn, &http2.ServeConnOpts{Handler: s.Handler})
|
s.s.ServeConn(conn, &http2.ServeConnOpts{
|
||||||
|
Context: r.Context(),
|
||||||
|
Handler: s.Handler,
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Handle Upgrade to h2c (RFC 7540 Section 3.2)
|
// Handle Upgrade to h2c (RFC 7540 Section 3.2)
|
||||||
if conn, err := h2cUpgrade(w, r); err == nil {
|
if conn, err := h2cUpgrade(w, r); err == nil {
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
s.s.ServeConn(conn, &http2.ServeConnOpts{Handler: s.Handler})
|
s.s.ServeConn(conn, &http2.ServeConnOpts{
|
||||||
|
Context: r.Context(),
|
||||||
|
Handler: s.Handler,
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
7
vendor/golang.org/x/net/http2/hpack/huffman.go
generated
vendored
7
vendor/golang.org/x/net/http2/hpack/huffman.go
generated
vendored
@ -105,7 +105,14 @@ func huffmanDecode(buf *bytes.Buffer, maxLen int, v []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// incomparable is a zero-width, non-comparable type. Adding it to a struct
|
||||||
|
// makes that struct also non-comparable, and generally doesn't add
|
||||||
|
// any size (as long as it's first).
|
||||||
|
type incomparable [0]func()
|
||||||
|
|
||||||
type node struct {
|
type node struct {
|
||||||
|
_ incomparable
|
||||||
|
|
||||||
// children is non-nil for internal nodes
|
// children is non-nil for internal nodes
|
||||||
children *[256]*node
|
children *[256]*node
|
||||||
|
|
||||||
|
7
vendor/golang.org/x/net/http2/http2.go
generated
vendored
7
vendor/golang.org/x/net/http2/http2.go
generated
vendored
@ -241,6 +241,7 @@ func (cw closeWaiter) Wait() {
|
|||||||
// Its buffered writer is lazily allocated as needed, to minimize
|
// Its buffered writer is lazily allocated as needed, to minimize
|
||||||
// idle memory usage with many connections.
|
// idle memory usage with many connections.
|
||||||
type bufferedWriter struct {
|
type bufferedWriter struct {
|
||||||
|
_ incomparable
|
||||||
w io.Writer // immutable
|
w io.Writer // immutable
|
||||||
bw *bufio.Writer // non-nil when data is buffered
|
bw *bufio.Writer // non-nil when data is buffered
|
||||||
}
|
}
|
||||||
@ -313,6 +314,7 @@ func bodyAllowedForStatus(status int) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type httpError struct {
|
type httpError struct {
|
||||||
|
_ incomparable
|
||||||
msg string
|
msg string
|
||||||
timeout bool
|
timeout bool
|
||||||
}
|
}
|
||||||
@ -376,3 +378,8 @@ func (s *sorter) SortStrings(ss []string) {
|
|||||||
func validPseudoPath(v string) bool {
|
func validPseudoPath(v string) bool {
|
||||||
return (len(v) > 0 && v[0] == '/') || v == "*"
|
return (len(v) > 0 && v[0] == '/') || v == "*"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// incomparable is a zero-width, non-comparable type. Adding it to a struct
|
||||||
|
// makes that struct also non-comparable, and generally doesn't add
|
||||||
|
// any size (as long as it's first).
|
||||||
|
type incomparable [0]func()
|
||||||
|
1
vendor/golang.org/x/net/http2/not_go111.go
generated
vendored
1
vendor/golang.org/x/net/http2/not_go111.go
generated
vendored
@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !go1.11
|
||||||
// +build !go1.11
|
// +build !go1.11
|
||||||
|
|
||||||
package http2
|
package http2
|
||||||
|
50
vendor/golang.org/x/net/http2/server.go
generated
vendored
50
vendor/golang.org/x/net/http2/server.go
generated
vendored
@ -761,6 +761,7 @@ func (sc *serverConn) readFrames() {
|
|||||||
|
|
||||||
// frameWriteResult is the message passed from writeFrameAsync to the serve goroutine.
|
// frameWriteResult is the message passed from writeFrameAsync to the serve goroutine.
|
||||||
type frameWriteResult struct {
|
type frameWriteResult struct {
|
||||||
|
_ incomparable
|
||||||
wr FrameWriteRequest // what was written (or attempted)
|
wr FrameWriteRequest // what was written (or attempted)
|
||||||
err error // result of the writeFrame call
|
err error // result of the writeFrame call
|
||||||
}
|
}
|
||||||
@ -771,7 +772,7 @@ type frameWriteResult struct {
|
|||||||
// serverConn.
|
// serverConn.
|
||||||
func (sc *serverConn) writeFrameAsync(wr FrameWriteRequest) {
|
func (sc *serverConn) writeFrameAsync(wr FrameWriteRequest) {
|
||||||
err := wr.write.writeFrame(sc)
|
err := wr.write.writeFrame(sc)
|
||||||
sc.wroteFrameCh <- frameWriteResult{wr, err}
|
sc.wroteFrameCh <- frameWriteResult{wr: wr, err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sc *serverConn) closeAllStreamsOnConnClose() {
|
func (sc *serverConn) closeAllStreamsOnConnClose() {
|
||||||
@ -1161,7 +1162,7 @@ func (sc *serverConn) startFrameWrite(wr FrameWriteRequest) {
|
|||||||
if wr.write.staysWithinBuffer(sc.bw.Available()) {
|
if wr.write.staysWithinBuffer(sc.bw.Available()) {
|
||||||
sc.writingFrameAsync = false
|
sc.writingFrameAsync = false
|
||||||
err := wr.write.writeFrame(sc)
|
err := wr.write.writeFrame(sc)
|
||||||
sc.wroteFrame(frameWriteResult{wr, err})
|
sc.wroteFrame(frameWriteResult{wr: wr, err: err})
|
||||||
} else {
|
} else {
|
||||||
sc.writingFrameAsync = true
|
sc.writingFrameAsync = true
|
||||||
go sc.writeFrameAsync(wr)
|
go sc.writeFrameAsync(wr)
|
||||||
@ -1292,7 +1293,9 @@ func (sc *serverConn) startGracefulShutdown() {
|
|||||||
sc.shutdownOnce.Do(func() { sc.sendServeMsg(gracefulShutdownMsg) })
|
sc.shutdownOnce.Do(func() { sc.sendServeMsg(gracefulShutdownMsg) })
|
||||||
}
|
}
|
||||||
|
|
||||||
// After sending GOAWAY, the connection will close after goAwayTimeout.
|
// After sending GOAWAY with an error code (non-graceful shutdown), the
|
||||||
|
// connection will close after goAwayTimeout.
|
||||||
|
//
|
||||||
// If we close the connection immediately after sending GOAWAY, there may
|
// If we close the connection immediately after sending GOAWAY, there may
|
||||||
// be unsent data in our kernel receive buffer, which will cause the kernel
|
// be unsent data in our kernel receive buffer, which will cause the kernel
|
||||||
// to send a TCP RST on close() instead of a FIN. This RST will abort the
|
// to send a TCP RST on close() instead of a FIN. This RST will abort the
|
||||||
@ -1628,23 +1631,37 @@ func (sc *serverConn) processSettingInitialWindowSize(val uint32) error {
|
|||||||
|
|
||||||
func (sc *serverConn) processData(f *DataFrame) error {
|
func (sc *serverConn) processData(f *DataFrame) error {
|
||||||
sc.serveG.check()
|
sc.serveG.check()
|
||||||
if sc.inGoAway && sc.goAwayCode != ErrCodeNo {
|
id := f.Header().StreamID
|
||||||
|
if sc.inGoAway && (sc.goAwayCode != ErrCodeNo || id > sc.maxClientStreamID) {
|
||||||
|
// Discard all DATA frames if the GOAWAY is due to an
|
||||||
|
// error, or:
|
||||||
|
//
|
||||||
|
// Section 6.8: After sending a GOAWAY frame, the sender
|
||||||
|
// can discard frames for streams initiated by the
|
||||||
|
// receiver with identifiers higher than the identified
|
||||||
|
// last stream.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
data := f.Data()
|
|
||||||
|
|
||||||
// "If a DATA frame is received whose stream is not in "open"
|
data := f.Data()
|
||||||
// or "half closed (local)" state, the recipient MUST respond
|
|
||||||
// with a stream error (Section 5.4.2) of type STREAM_CLOSED."
|
|
||||||
id := f.Header().StreamID
|
|
||||||
state, st := sc.state(id)
|
state, st := sc.state(id)
|
||||||
if id == 0 || state == stateIdle {
|
if id == 0 || state == stateIdle {
|
||||||
|
// Section 6.1: "DATA frames MUST be associated with a
|
||||||
|
// stream. If a DATA frame is received whose stream
|
||||||
|
// identifier field is 0x0, the recipient MUST respond
|
||||||
|
// with a connection error (Section 5.4.1) of type
|
||||||
|
// PROTOCOL_ERROR."
|
||||||
|
//
|
||||||
// Section 5.1: "Receiving any frame other than HEADERS
|
// Section 5.1: "Receiving any frame other than HEADERS
|
||||||
// or PRIORITY on a stream in this state MUST be
|
// or PRIORITY on a stream in this state MUST be
|
||||||
// treated as a connection error (Section 5.4.1) of
|
// treated as a connection error (Section 5.4.1) of
|
||||||
// type PROTOCOL_ERROR."
|
// type PROTOCOL_ERROR."
|
||||||
return ConnectionError(ErrCodeProtocol)
|
return ConnectionError(ErrCodeProtocol)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// "If a DATA frame is received whose stream is not in "open"
|
||||||
|
// or "half closed (local)" state, the recipient MUST respond
|
||||||
|
// with a stream error (Section 5.4.2) of type STREAM_CLOSED."
|
||||||
if st == nil || state != stateOpen || st.gotTrailerHeader || st.resetQueued {
|
if st == nil || state != stateOpen || st.gotTrailerHeader || st.resetQueued {
|
||||||
// This includes sending a RST_STREAM if the stream is
|
// This includes sending a RST_STREAM if the stream is
|
||||||
// in stateHalfClosedLocal (which currently means that
|
// in stateHalfClosedLocal (which currently means that
|
||||||
@ -1693,6 +1710,7 @@ func (sc *serverConn) processData(f *DataFrame) error {
|
|||||||
if len(data) > 0 {
|
if len(data) > 0 {
|
||||||
wrote, err := st.body.Write(data)
|
wrote, err := st.body.Write(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
sc.sendWindowUpdate(nil, int(f.Length)-wrote)
|
||||||
return streamError(id, ErrCodeStreamClosed)
|
return streamError(id, ErrCodeStreamClosed)
|
||||||
}
|
}
|
||||||
if wrote != len(data) {
|
if wrote != len(data) {
|
||||||
@ -2019,7 +2037,11 @@ func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*res
|
|||||||
}
|
}
|
||||||
if bodyOpen {
|
if bodyOpen {
|
||||||
if vv, ok := rp.header["Content-Length"]; ok {
|
if vv, ok := rp.header["Content-Length"]; ok {
|
||||||
req.ContentLength, _ = strconv.ParseInt(vv[0], 10, 64)
|
if cl, err := strconv.ParseUint(vv[0], 10, 63); err == nil {
|
||||||
|
req.ContentLength = int64(cl)
|
||||||
|
} else {
|
||||||
|
req.ContentLength = 0
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
req.ContentLength = -1
|
req.ContentLength = -1
|
||||||
}
|
}
|
||||||
@ -2057,7 +2079,7 @@ func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp requestParam) (*r
|
|||||||
var trailer http.Header
|
var trailer http.Header
|
||||||
for _, v := range rp.header["Trailer"] {
|
for _, v := range rp.header["Trailer"] {
|
||||||
for _, key := range strings.Split(v, ",") {
|
for _, key := range strings.Split(v, ",") {
|
||||||
key = http.CanonicalHeaderKey(strings.TrimSpace(key))
|
key = http.CanonicalHeaderKey(textproto.TrimString(key))
|
||||||
switch key {
|
switch key {
|
||||||
case "Transfer-Encoding", "Trailer", "Content-Length":
|
case "Transfer-Encoding", "Trailer", "Content-Length":
|
||||||
// Bogus. (copy of http1 rules)
|
// Bogus. (copy of http1 rules)
|
||||||
@ -2275,6 +2297,7 @@ func (sc *serverConn) sendWindowUpdate32(st *stream, n int32) {
|
|||||||
// requestBody is the Handler's Request.Body type.
|
// requestBody is the Handler's Request.Body type.
|
||||||
// Read and Close may be called concurrently.
|
// Read and Close may be called concurrently.
|
||||||
type requestBody struct {
|
type requestBody struct {
|
||||||
|
_ incomparable
|
||||||
stream *stream
|
stream *stream
|
||||||
conn *serverConn
|
conn *serverConn
|
||||||
closed bool // for use by Close only
|
closed bool // for use by Close only
|
||||||
@ -2401,9 +2424,8 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
|
|||||||
var ctype, clen string
|
var ctype, clen string
|
||||||
if clen = rws.snapHeader.Get("Content-Length"); clen != "" {
|
if clen = rws.snapHeader.Get("Content-Length"); clen != "" {
|
||||||
rws.snapHeader.Del("Content-Length")
|
rws.snapHeader.Del("Content-Length")
|
||||||
clen64, err := strconv.ParseInt(clen, 10, 64)
|
if cl, err := strconv.ParseUint(clen, 10, 63); err == nil {
|
||||||
if err == nil && clen64 >= 0 {
|
rws.sentContentLen = int64(cl)
|
||||||
rws.sentContentLen = clen64
|
|
||||||
} else {
|
} else {
|
||||||
clen = ""
|
clen = ""
|
||||||
}
|
}
|
||||||
|
118
vendor/golang.org/x/net/http2/transport.go
generated
vendored
118
vendor/golang.org/x/net/http2/transport.go
generated
vendored
@ -108,6 +108,19 @@ type Transport struct {
|
|||||||
// waiting for their turn.
|
// waiting for their turn.
|
||||||
StrictMaxConcurrentStreams bool
|
StrictMaxConcurrentStreams bool
|
||||||
|
|
||||||
|
// ReadIdleTimeout is the timeout after which a health check using ping
|
||||||
|
// frame will be carried out if no frame is received on the connection.
|
||||||
|
// Note that a ping response will is considered a received frame, so if
|
||||||
|
// there is no other traffic on the connection, the health check will
|
||||||
|
// be performed every ReadIdleTimeout interval.
|
||||||
|
// If zero, no health check is performed.
|
||||||
|
ReadIdleTimeout time.Duration
|
||||||
|
|
||||||
|
// PingTimeout is the timeout after which the connection will be closed
|
||||||
|
// if a response to Ping is not received.
|
||||||
|
// Defaults to 15s.
|
||||||
|
PingTimeout time.Duration
|
||||||
|
|
||||||
// t1, if non-nil, is the standard library Transport using
|
// t1, if non-nil, is the standard library Transport using
|
||||||
// this transport. Its settings are used (but not its
|
// this transport. Its settings are used (but not its
|
||||||
// RoundTrip method, etc).
|
// RoundTrip method, etc).
|
||||||
@ -131,14 +144,31 @@ func (t *Transport) disableCompression() bool {
|
|||||||
return t.DisableCompression || (t.t1 != nil && t.t1.DisableCompression)
|
return t.DisableCompression || (t.t1 != nil && t.t1.DisableCompression)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Transport) pingTimeout() time.Duration {
|
||||||
|
if t.PingTimeout == 0 {
|
||||||
|
return 15 * time.Second
|
||||||
|
}
|
||||||
|
return t.PingTimeout
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// ConfigureTransport configures a net/http HTTP/1 Transport to use HTTP/2.
|
// ConfigureTransport configures a net/http HTTP/1 Transport to use HTTP/2.
|
||||||
// It returns an error if t1 has already been HTTP/2-enabled.
|
// It returns an error if t1 has already been HTTP/2-enabled.
|
||||||
|
//
|
||||||
|
// Use ConfigureTransports instead to configure the HTTP/2 Transport.
|
||||||
func ConfigureTransport(t1 *http.Transport) error {
|
func ConfigureTransport(t1 *http.Transport) error {
|
||||||
_, err := configureTransport(t1)
|
_, err := ConfigureTransports(t1)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func configureTransport(t1 *http.Transport) (*Transport, error) {
|
// ConfigureTransports configures a net/http HTTP/1 Transport to use HTTP/2.
|
||||||
|
// It returns a new HTTP/2 Transport for further configuration.
|
||||||
|
// It returns an error if t1 has already been HTTP/2-enabled.
|
||||||
|
func ConfigureTransports(t1 *http.Transport) (*Transport, error) {
|
||||||
|
return configureTransports(t1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func configureTransports(t1 *http.Transport) (*Transport, error) {
|
||||||
connPool := new(clientConnPool)
|
connPool := new(clientConnPool)
|
||||||
t2 := &Transport{
|
t2 := &Transport{
|
||||||
ConnPool: noDialClientConnPool{connPool},
|
ConnPool: noDialClientConnPool{connPool},
|
||||||
@ -668,6 +698,7 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
|
|||||||
cc.inflow.add(transportDefaultConnFlow + initialWindowSize)
|
cc.inflow.add(transportDefaultConnFlow + initialWindowSize)
|
||||||
cc.bw.Flush()
|
cc.bw.Flush()
|
||||||
if cc.werr != nil {
|
if cc.werr != nil {
|
||||||
|
cc.Close()
|
||||||
return nil, cc.werr
|
return nil, cc.werr
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -675,6 +706,20 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
|
|||||||
return cc, nil
|
return cc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cc *ClientConn) healthCheck() {
|
||||||
|
pingTimeout := cc.t.pingTimeout()
|
||||||
|
// We don't need to periodically ping in the health check, because the readLoop of ClientConn will
|
||||||
|
// trigger the healthCheck again if there is no frame received.
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), pingTimeout)
|
||||||
|
defer cancel()
|
||||||
|
err := cc.Ping(ctx)
|
||||||
|
if err != nil {
|
||||||
|
cc.closeForLostPing()
|
||||||
|
cc.t.connPool().MarkDead(cc)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (cc *ClientConn) setGoAway(f *GoAwayFrame) {
|
func (cc *ClientConn) setGoAway(f *GoAwayFrame) {
|
||||||
cc.mu.Lock()
|
cc.mu.Lock()
|
||||||
defer cc.mu.Unlock()
|
defer cc.mu.Unlock()
|
||||||
@ -846,14 +891,12 @@ func (cc *ClientConn) sendGoAway() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the client connection immediately.
|
// closes the client connection immediately. In-flight requests are interrupted.
|
||||||
//
|
// err is sent to streams.
|
||||||
// In-flight requests are interrupted. For a graceful shutdown, use Shutdown instead.
|
func (cc *ClientConn) closeForError(err error) error {
|
||||||
func (cc *ClientConn) Close() error {
|
|
||||||
cc.mu.Lock()
|
cc.mu.Lock()
|
||||||
defer cc.cond.Broadcast()
|
defer cc.cond.Broadcast()
|
||||||
defer cc.mu.Unlock()
|
defer cc.mu.Unlock()
|
||||||
err := errors.New("http2: client connection force closed via ClientConn.Close")
|
|
||||||
for id, cs := range cc.streams {
|
for id, cs := range cc.streams {
|
||||||
select {
|
select {
|
||||||
case cs.resc <- resAndError{err: err}:
|
case cs.resc <- resAndError{err: err}:
|
||||||
@ -866,6 +909,20 @@ func (cc *ClientConn) Close() error {
|
|||||||
return cc.tconn.Close()
|
return cc.tconn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close closes the client connection immediately.
|
||||||
|
//
|
||||||
|
// In-flight requests are interrupted. For a graceful shutdown, use Shutdown instead.
|
||||||
|
func (cc *ClientConn) Close() error {
|
||||||
|
err := errors.New("http2: client connection force closed via ClientConn.Close")
|
||||||
|
return cc.closeForError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// closes the client connection immediately. In-flight requests are interrupted.
|
||||||
|
func (cc *ClientConn) closeForLostPing() error {
|
||||||
|
err := errors.New("http2: client connection lost")
|
||||||
|
return cc.closeForError(err)
|
||||||
|
}
|
||||||
|
|
||||||
const maxAllocFrameSize = 512 << 10
|
const maxAllocFrameSize = 512 << 10
|
||||||
|
|
||||||
// frameBuffer returns a scratch buffer suitable for writing DATA frames.
|
// frameBuffer returns a scratch buffer suitable for writing DATA frames.
|
||||||
@ -916,7 +973,7 @@ func commaSeparatedTrailers(req *http.Request) (string, error) {
|
|||||||
k = http.CanonicalHeaderKey(k)
|
k = http.CanonicalHeaderKey(k)
|
||||||
switch k {
|
switch k {
|
||||||
case "Transfer-Encoding", "Trailer", "Content-Length":
|
case "Transfer-Encoding", "Trailer", "Content-Length":
|
||||||
return "", &badStringError{"invalid Trailer key", k}
|
return "", fmt.Errorf("invalid Trailer key %q", k)
|
||||||
}
|
}
|
||||||
keys = append(keys, k)
|
keys = append(keys, k)
|
||||||
}
|
}
|
||||||
@ -1033,6 +1090,15 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
|
|||||||
bodyWriter := cc.t.getBodyWriterState(cs, body)
|
bodyWriter := cc.t.getBodyWriterState(cs, body)
|
||||||
cs.on100 = bodyWriter.on100
|
cs.on100 = bodyWriter.on100
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
cc.wmu.Lock()
|
||||||
|
werr := cc.werr
|
||||||
|
cc.wmu.Unlock()
|
||||||
|
if werr != nil {
|
||||||
|
cc.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
cc.wmu.Lock()
|
cc.wmu.Lock()
|
||||||
endStream := !hasBody && !hasTrailers
|
endStream := !hasBody && !hasTrailers
|
||||||
werr := cc.writeHeaders(cs.ID, endStream, int(cc.maxFrameSize), hdrs)
|
werr := cc.writeHeaders(cs.ID, endStream, int(cc.maxFrameSize), hdrs)
|
||||||
@ -1082,6 +1148,9 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
|
|||||||
// we can keep it.
|
// we can keep it.
|
||||||
bodyWriter.cancel()
|
bodyWriter.cancel()
|
||||||
cs.abortRequestBodyWrite(errStopReqBodyWrite)
|
cs.abortRequestBodyWrite(errStopReqBodyWrite)
|
||||||
|
if hasBody && !bodyWritten {
|
||||||
|
<-bodyWriter.resc
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if re.err != nil {
|
if re.err != nil {
|
||||||
cc.forgetStreamID(cs.ID)
|
cc.forgetStreamID(cs.ID)
|
||||||
@ -1102,6 +1171,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
|
|||||||
} else {
|
} else {
|
||||||
bodyWriter.cancel()
|
bodyWriter.cancel()
|
||||||
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
|
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
|
||||||
|
<-bodyWriter.resc
|
||||||
}
|
}
|
||||||
cc.forgetStreamID(cs.ID)
|
cc.forgetStreamID(cs.ID)
|
||||||
return nil, cs.getStartedWrite(), errTimeout
|
return nil, cs.getStartedWrite(), errTimeout
|
||||||
@ -1111,6 +1181,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
|
|||||||
} else {
|
} else {
|
||||||
bodyWriter.cancel()
|
bodyWriter.cancel()
|
||||||
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
|
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
|
||||||
|
<-bodyWriter.resc
|
||||||
}
|
}
|
||||||
cc.forgetStreamID(cs.ID)
|
cc.forgetStreamID(cs.ID)
|
||||||
return nil, cs.getStartedWrite(), ctx.Err()
|
return nil, cs.getStartedWrite(), ctx.Err()
|
||||||
@ -1120,6 +1191,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
|
|||||||
} else {
|
} else {
|
||||||
bodyWriter.cancel()
|
bodyWriter.cancel()
|
||||||
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
|
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
|
||||||
|
<-bodyWriter.resc
|
||||||
}
|
}
|
||||||
cc.forgetStreamID(cs.ID)
|
cc.forgetStreamID(cs.ID)
|
||||||
return nil, cs.getStartedWrite(), errRequestCanceled
|
return nil, cs.getStartedWrite(), errRequestCanceled
|
||||||
@ -1129,6 +1201,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
|
|||||||
// forgetStreamID.
|
// forgetStreamID.
|
||||||
return nil, cs.getStartedWrite(), cs.resetErr
|
return nil, cs.getStartedWrite(), cs.resetErr
|
||||||
case err := <-bodyWriter.resc:
|
case err := <-bodyWriter.resc:
|
||||||
|
bodyWritten = true
|
||||||
// Prefer the read loop's response, if available. Issue 16102.
|
// Prefer the read loop's response, if available. Issue 16102.
|
||||||
select {
|
select {
|
||||||
case re := <-readLoopResCh:
|
case re := <-readLoopResCh:
|
||||||
@ -1139,7 +1212,6 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
|
|||||||
cc.forgetStreamID(cs.ID)
|
cc.forgetStreamID(cs.ID)
|
||||||
return nil, cs.getStartedWrite(), err
|
return nil, cs.getStartedWrite(), err
|
||||||
}
|
}
|
||||||
bodyWritten = true
|
|
||||||
if d := cc.responseHeaderTimeout(); d != 0 {
|
if d := cc.responseHeaderTimeout(); d != 0 {
|
||||||
timer := time.NewTimer(d)
|
timer := time.NewTimer(d)
|
||||||
defer timer.Stop()
|
defer timer.Stop()
|
||||||
@ -1394,13 +1466,6 @@ func (cs *clientStream) awaitFlowControl(maxBytes int) (taken int32, err error)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type badStringError struct {
|
|
||||||
what string
|
|
||||||
str string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *badStringError) Error() string { return fmt.Sprintf("%s %q", e.what, e.str) }
|
|
||||||
|
|
||||||
// requires cc.mu be held.
|
// requires cc.mu be held.
|
||||||
func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trailers string, contentLength int64) ([]byte, error) {
|
func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trailers string, contentLength int64) ([]byte, error) {
|
||||||
cc.hbuf.Reset()
|
cc.hbuf.Reset()
|
||||||
@ -1616,6 +1681,7 @@ func (cc *ClientConn) writeHeader(name, value string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type resAndError struct {
|
type resAndError struct {
|
||||||
|
_ incomparable
|
||||||
res *http.Response
|
res *http.Response
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
@ -1663,6 +1729,7 @@ func (cc *ClientConn) streamByID(id uint32, andRemove bool) *clientStream {
|
|||||||
|
|
||||||
// clientConnReadLoop is the state owned by the clientConn's frame-reading readLoop.
|
// clientConnReadLoop is the state owned by the clientConn's frame-reading readLoop.
|
||||||
type clientConnReadLoop struct {
|
type clientConnReadLoop struct {
|
||||||
|
_ incomparable
|
||||||
cc *ClientConn
|
cc *ClientConn
|
||||||
closeWhenIdle bool
|
closeWhenIdle bool
|
||||||
}
|
}
|
||||||
@ -1742,8 +1809,17 @@ func (rl *clientConnReadLoop) run() error {
|
|||||||
rl.closeWhenIdle = cc.t.disableKeepAlives() || cc.singleUse
|
rl.closeWhenIdle = cc.t.disableKeepAlives() || cc.singleUse
|
||||||
gotReply := false // ever saw a HEADERS reply
|
gotReply := false // ever saw a HEADERS reply
|
||||||
gotSettings := false
|
gotSettings := false
|
||||||
|
readIdleTimeout := cc.t.ReadIdleTimeout
|
||||||
|
var t *time.Timer
|
||||||
|
if readIdleTimeout != 0 {
|
||||||
|
t = time.AfterFunc(readIdleTimeout, cc.healthCheck)
|
||||||
|
defer t.Stop()
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
f, err := cc.fr.ReadFrame()
|
f, err := cc.fr.ReadFrame()
|
||||||
|
if t != nil {
|
||||||
|
t.Reset(readIdleTimeout)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cc.vlogf("http2: Transport readFrame error on conn %p: (%T) %v", cc, err, err)
|
cc.vlogf("http2: Transport readFrame error on conn %p: (%T) %v", cc, err, err)
|
||||||
}
|
}
|
||||||
@ -1955,8 +2031,8 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
|
|||||||
if !streamEnded || isHead {
|
if !streamEnded || isHead {
|
||||||
res.ContentLength = -1
|
res.ContentLength = -1
|
||||||
if clens := res.Header["Content-Length"]; len(clens) == 1 {
|
if clens := res.Header["Content-Length"]; len(clens) == 1 {
|
||||||
if clen64, err := strconv.ParseInt(clens[0], 10, 64); err == nil {
|
if cl, err := strconv.ParseUint(clens[0], 10, 63); err == nil {
|
||||||
res.ContentLength = clen64
|
res.ContentLength = int64(cl)
|
||||||
} else {
|
} else {
|
||||||
// TODO: care? unlike http/1, it won't mess up our framing, so it's
|
// TODO: care? unlike http/1, it won't mess up our framing, so it's
|
||||||
// more safe smuggling-wise to ignore.
|
// more safe smuggling-wise to ignore.
|
||||||
@ -2474,11 +2550,13 @@ func strSliceContains(ss []string, s string) bool {
|
|||||||
|
|
||||||
type erringRoundTripper struct{ err error }
|
type erringRoundTripper struct{ err error }
|
||||||
|
|
||||||
|
func (rt erringRoundTripper) RoundTripErr() error { return rt.err }
|
||||||
func (rt erringRoundTripper) RoundTrip(*http.Request) (*http.Response, error) { return nil, rt.err }
|
func (rt erringRoundTripper) RoundTrip(*http.Request) (*http.Response, error) { return nil, rt.err }
|
||||||
|
|
||||||
// gzipReader wraps a response body so it can lazily
|
// gzipReader wraps a response body so it can lazily
|
||||||
// call gzip.NewReader on the first call to Read
|
// call gzip.NewReader on the first call to Read
|
||||||
type gzipReader struct {
|
type gzipReader struct {
|
||||||
|
_ incomparable
|
||||||
body io.ReadCloser // underlying Response.Body
|
body io.ReadCloser // underlying Response.Body
|
||||||
zr *gzip.Reader // lazily-initialized gzip reader
|
zr *gzip.Reader // lazily-initialized gzip reader
|
||||||
zerr error // sticky error
|
zerr error // sticky error
|
||||||
@ -2554,7 +2632,9 @@ func (t *Transport) getBodyWriterState(cs *clientStream, body io.Reader) (s body
|
|||||||
|
|
||||||
func (s bodyWriterState) cancel() {
|
func (s bodyWriterState) cancel() {
|
||||||
if s.timer != nil {
|
if s.timer != nil {
|
||||||
s.timer.Stop()
|
if s.timer.Stop() {
|
||||||
|
s.resc <- nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
vendor/golang.org/x/net/idna/idna10.0.0.go
generated
vendored
1
vendor/golang.org/x/net/idna/idna10.0.0.go
generated
vendored
@ -4,6 +4,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build go1.10
|
||||||
// +build go1.10
|
// +build go1.10
|
||||||
|
|
||||||
// Package idna implements IDNA2008 using the compatibility processing
|
// Package idna implements IDNA2008 using the compatibility processing
|
||||||
|
1
vendor/golang.org/x/net/idna/idna9.0.0.go
generated
vendored
1
vendor/golang.org/x/net/idna/idna9.0.0.go
generated
vendored
@ -4,6 +4,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !go1.10
|
||||||
// +build !go1.10
|
// +build !go1.10
|
||||||
|
|
||||||
// Package idna implements IDNA2008 using the compatibility processing
|
// Package idna implements IDNA2008 using the compatibility processing
|
||||||
|
1
vendor/golang.org/x/net/idna/tables10.0.0.go
generated
vendored
1
vendor/golang.org/x/net/idna/tables10.0.0.go
generated
vendored
@ -1,5 +1,6 @@
|
|||||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build go1.10 && !go1.13
|
||||||
// +build go1.10,!go1.13
|
// +build go1.10,!go1.13
|
||||||
|
|
||||||
package idna
|
package idna
|
||||||
|
1
vendor/golang.org/x/net/idna/tables11.0.0.go
generated
vendored
1
vendor/golang.org/x/net/idna/tables11.0.0.go
generated
vendored
@ -1,5 +1,6 @@
|
|||||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build go1.13 && !go1.14
|
||||||
// +build go1.13,!go1.14
|
// +build go1.13,!go1.14
|
||||||
|
|
||||||
package idna
|
package idna
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||||
|
|
||||||
// +build go1.14
|
//go:build go1.14 && !go1.16
|
||||||
|
// +build go1.14,!go1.16
|
||||||
|
|
||||||
package idna
|
package idna
|
||||||
|
|
4840
vendor/golang.org/x/net/idna/tables13.0.0.go
generated
vendored
Normal file
4840
vendor/golang.org/x/net/idna/tables13.0.0.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user