Make it work with modules
This commit is contained in:
parent
0b1212f428
commit
ddc3477efd
3843
frontend/package-lock.json
generated
3843
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -1,3 +1 @@
|
||||||
{
|
{"API_URL": "/api/v1/"}
|
||||||
"API_URL": "http://localhost:8082/api/v1/"
|
|
||||||
}
|
|
||||||
|
|
32
go.mod
Normal file
32
go.mod
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
module git.kolaente.de/konrad/Library
|
||||||
|
|
||||||
|
go 1.16
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1-0.20171005155431-ecdeabc65495 // indirect
|
||||||
|
github.com/denisenkom/go-mssqldb v0.10.0 // indirect
|
||||||
|
github.com/dgrijalva/jwt-go v3.0.1-0.20170608005149-a539ee1a749a+incompatible
|
||||||
|
github.com/go-ini/ini v1.28.2
|
||||||
|
github.com/go-sql-driver/mysql v1.3.1-0.20171007150158-ee359f95877b
|
||||||
|
github.com/go-xorm/builder v0.0.0-20170519032130-c8871c857d25 // indirect
|
||||||
|
github.com/go-xorm/core v0.5.7
|
||||||
|
github.com/go-xorm/xorm v0.6.4-0.20170930012613-29d4a0330a00
|
||||||
|
github.com/joho/godotenv v1.3.0 // indirect
|
||||||
|
github.com/labstack/echo v3.1.1-0.20170426170929-1049c9613cd3+incompatible
|
||||||
|
github.com/labstack/gommon v0.2.2-0.20170925052817-57409ada9da0 // indirect
|
||||||
|
github.com/lib/pq v1.10.1 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.0.10-0.20170816031813-ad5389df28cd // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.4-0.20170925054904-a5cdd64afdee // indirect
|
||||||
|
github.com/mattn/go-oci8 v0.1.1 // indirect
|
||||||
|
github.com/mattn/go-sqlite3 v1.5.0
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/smartystreets/goconvey v1.6.4 // indirect
|
||||||
|
github.com/stretchr/testify v1.2.1-0.20171231124224-87b1dfb5b2fa
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
|
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 // indirect
|
||||||
|
github.com/ziutek/mymysql v1.5.4 // indirect
|
||||||
|
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||||
|
gopkg.in/testfixtures.v2 v2.4.3
|
||||||
|
gopkg.in/yaml.v2 v2.0.0 // indirect
|
||||||
|
)
|
72
go.sum
Normal file
72
go.sum
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.1-0.20171005155431-ecdeabc65495 h1:b2hEFhj0PgDc77eCeDUSKXynIoXJRt6yTZ8aMk2cPoI=
|
||||||
|
github.com/davecgh/go-spew v1.1.1-0.20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/denisenkom/go-mssqldb v0.10.0 h1:QykgLZBorFE95+gO3u9esLd0BmbvpWp0/waNNZfHBM8=
|
||||||
|
github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||||
|
github.com/dgrijalva/jwt-go v3.0.1-0.20170608005149-a539ee1a749a+incompatible h1:lwWnUpbS8H6DbUpe9VIY8G0vOupN8pnCPs68g9oxAJI=
|
||||||
|
github.com/dgrijalva/jwt-go v3.0.1-0.20170608005149-a539ee1a749a+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
|
github.com/go-ini/ini v1.28.2 h1:drmmYv7psRpoGZkPtPKKTB+ZFSnvmwCMfNj5o1nLh2Y=
|
||||||
|
github.com/go-ini/ini v1.28.2/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||||
|
github.com/go-sql-driver/mysql v1.3.1-0.20171007150158-ee359f95877b h1:U876wVumr5JIhbkg6n/bjYkgR2VwX9f/GjvHoz9tBsw=
|
||||||
|
github.com/go-sql-driver/mysql v1.3.1-0.20171007150158-ee359f95877b/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
|
github.com/go-xorm/builder v0.0.0-20170519032130-c8871c857d25 h1:jUX9yw6+iKrs/WuysV2M6ap/ObK/07SE/a7I2uxitwM=
|
||||||
|
github.com/go-xorm/builder v0.0.0-20170519032130-c8871c857d25/go.mod h1:M+P3wv0K2C+ynucGDEqJCeOTc+6DcAtiiqU8GrCksXY=
|
||||||
|
github.com/go-xorm/core v0.5.7 h1:ClaJQDjHDre5Yco2MmkWKniM8NNdC/OXmoy2HfxxECw=
|
||||||
|
github.com/go-xorm/core v0.5.7/go.mod h1:i7QESCABdFcvhgc8pdINtzlJf/6LC29if6ZJgHt9SHI=
|
||||||
|
github.com/go-xorm/xorm v0.6.4-0.20170930012613-29d4a0330a00 h1:sryNK0GCJOjs3WNgdCMjr7AuFrF4pYf9LrQcomTg7k8=
|
||||||
|
github.com/go-xorm/xorm v0.6.4-0.20170930012613-29d4a0330a00/go.mod h1:i7qRPD38xj/v75UV+a9pEzr5tfRaH2ndJfwt/fGbQhs=
|
||||||
|
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/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/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||||
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
|
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/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||||
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/labstack/echo v3.1.1-0.20170426170929-1049c9613cd3+incompatible h1:mbe/VB+HbK7DFlOfYUlDOP9m28P64NFSqiHNpPu4h/Q=
|
||||||
|
github.com/labstack/echo v3.1.1-0.20170426170929-1049c9613cd3+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
|
||||||
|
github.com/labstack/gommon v0.2.2-0.20170925052817-57409ada9da0 h1:7AIW1qc9sYYTZLamTsRKSmVvJDXkZZrIWXHDK4Gq4X0=
|
||||||
|
github.com/labstack/gommon v0.2.2-0.20170925052817-57409ada9da0/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
|
||||||
|
github.com/lib/pq v1.10.1 h1:6VXZrLU0jHBYyAqrSPa+MgPfnSvTPuMgK+k0o5kVFWo=
|
||||||
|
github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
|
github.com/mattn/go-colorable v0.0.10-0.20170816031813-ad5389df28cd h1:Y4ZRx+RIPFlPL4gnD/I7bdqSNXHlNop1Q6NjQuHds00=
|
||||||
|
github.com/mattn/go-colorable v0.0.10-0.20170816031813-ad5389df28cd/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
|
github.com/mattn/go-isatty v0.0.4-0.20170925054904-a5cdd64afdee h1:L08yktFTj+MmaCAZBZKAU4EyW4hEDji2dPxLYJozx1s=
|
||||||
|
github.com/mattn/go-isatty v0.0.4-0.20170925054904-a5cdd64afdee/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
github.com/mattn/go-oci8 v0.1.1 h1:aEUDxNAyDG0tv8CA3TArnDQNyc4EhnWlsfxRgDHABHM=
|
||||||
|
github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI=
|
||||||
|
github.com/mattn/go-sqlite3 v1.5.0 h1:cD1JkMVOQgN+75Jni3VEkSwLkElfpfS194KbtOH9jX8=
|
||||||
|
github.com/mattn/go-sqlite3 v1.5.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||||
|
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/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
|
github.com/stretchr/testify v1.2.1-0.20171231124224-87b1dfb5b2fa h1:umkGKiDLv+oYTelap19DADMEu2+JcsrhBnAydIELGAI=
|
||||||
|
github.com/stretchr/testify v1.2.1-0.20171231124224-87b1dfb5b2fa/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
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/fasttemplate v0.0.0-20170224212429-dcecefd839c4 h1:gKMu1Bf6QINDnvyZuTaACm9ofY+PRh+5vFz4oxBZeF8=
|
||||||
|
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4/go.mod h1:50wTf68f99/Zt14pr046Tgt3Lp2vLyFZKzbFXTOabXw=
|
||||||
|
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
||||||
|
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-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/testfixtures.v2 v2.4.3 h1:hURC7rEeqQPxZ2PUSscSYgUC5xAjd8NHOQumPxmILKo=
|
||||||
|
gopkg.in/testfixtures.v2 v2.4.3/go.mod h1:vyAq+MYCgNpR29qitQdLZhdbLFf4mR/2MFJRFoQZZ2M=
|
||||||
|
gopkg.in/yaml.v2 v2.0.0 h1:uUkhRGrsEyx/laRdeS6YIQKIys8pg+lRSRdVMTYjivs=
|
||||||
|
gopkg.in/yaml.v2 v2.0.0/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
4
vendor/github.com/dgrijalva/jwt-go/.gitignore
generated
vendored
Normal file
4
vendor/github.com/dgrijalva/jwt-go/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
.DS_Store
|
||||||
|
bin
|
||||||
|
|
||||||
|
|
13
vendor/github.com/dgrijalva/jwt-go/.travis.yml
generated
vendored
Normal file
13
vendor/github.com/dgrijalva/jwt-go/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
language: go
|
||||||
|
|
||||||
|
script:
|
||||||
|
- go vet ./...
|
||||||
|
- go test -v ./...
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.3
|
||||||
|
- 1.4
|
||||||
|
- 1.5
|
||||||
|
- 1.6
|
||||||
|
- 1.7
|
||||||
|
- tip
|
175
vendor/github.com/garyburd/redigo/LICENSE
generated
vendored
175
vendor/github.com/garyburd/redigo/LICENSE
generated
vendored
|
@ -1,175 +0,0 @@
|
||||||
|
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
54
vendor/github.com/garyburd/redigo/internal/commandinfo.go
generated
vendored
54
vendor/github.com/garyburd/redigo/internal/commandinfo.go
generated
vendored
|
@ -1,54 +0,0 @@
|
||||||
// Copyright 2014 Gary Burd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package internal // import "github.com/garyburd/redigo/internal"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
WatchState = 1 << iota
|
|
||||||
MultiState
|
|
||||||
SubscribeState
|
|
||||||
MonitorState
|
|
||||||
)
|
|
||||||
|
|
||||||
type CommandInfo struct {
|
|
||||||
Set, Clear int
|
|
||||||
}
|
|
||||||
|
|
||||||
var commandInfos = map[string]CommandInfo{
|
|
||||||
"WATCH": {Set: WatchState},
|
|
||||||
"UNWATCH": {Clear: WatchState},
|
|
||||||
"MULTI": {Set: MultiState},
|
|
||||||
"EXEC": {Clear: WatchState | MultiState},
|
|
||||||
"DISCARD": {Clear: WatchState | MultiState},
|
|
||||||
"PSUBSCRIBE": {Set: SubscribeState},
|
|
||||||
"SUBSCRIBE": {Set: SubscribeState},
|
|
||||||
"MONITOR": {Set: MonitorState},
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
for n, ci := range commandInfos {
|
|
||||||
commandInfos[strings.ToLower(n)] = ci
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func LookupCommandInfo(commandName string) CommandInfo {
|
|
||||||
if ci, ok := commandInfos[commandName]; ok {
|
|
||||||
return ci
|
|
||||||
}
|
|
||||||
return commandInfos[strings.ToUpper(commandName)]
|
|
||||||
}
|
|
651
vendor/github.com/garyburd/redigo/redis/conn.go
generated
vendored
651
vendor/github.com/garyburd/redigo/redis/conn.go
generated
vendored
|
@ -1,651 +0,0 @@
|
||||||
// Copyright 2012 Gary Burd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"crypto/tls"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"net/url"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// conn is the low-level implementation of Conn
|
|
||||||
type conn struct {
|
|
||||||
// Shared
|
|
||||||
mu sync.Mutex
|
|
||||||
pending int
|
|
||||||
err error
|
|
||||||
conn net.Conn
|
|
||||||
|
|
||||||
// Read
|
|
||||||
readTimeout time.Duration
|
|
||||||
br *bufio.Reader
|
|
||||||
|
|
||||||
// Write
|
|
||||||
writeTimeout time.Duration
|
|
||||||
bw *bufio.Writer
|
|
||||||
|
|
||||||
// Scratch space for formatting argument length.
|
|
||||||
// '*' or '$', length, "\r\n"
|
|
||||||
lenScratch [32]byte
|
|
||||||
|
|
||||||
// Scratch space for formatting integers and floats.
|
|
||||||
numScratch [40]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// DialTimeout acts like Dial but takes timeouts for establishing the
|
|
||||||
// connection to the server, writing a command and reading a reply.
|
|
||||||
//
|
|
||||||
// Deprecated: Use Dial with options instead.
|
|
||||||
func DialTimeout(network, address string, connectTimeout, readTimeout, writeTimeout time.Duration) (Conn, error) {
|
|
||||||
return Dial(network, address,
|
|
||||||
DialConnectTimeout(connectTimeout),
|
|
||||||
DialReadTimeout(readTimeout),
|
|
||||||
DialWriteTimeout(writeTimeout))
|
|
||||||
}
|
|
||||||
|
|
||||||
// DialOption specifies an option for dialing a Redis server.
|
|
||||||
type DialOption struct {
|
|
||||||
f func(*dialOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
type dialOptions struct {
|
|
||||||
readTimeout time.Duration
|
|
||||||
writeTimeout time.Duration
|
|
||||||
dialer *net.Dialer
|
|
||||||
dial func(network, addr string) (net.Conn, error)
|
|
||||||
db int
|
|
||||||
password string
|
|
||||||
useTLS bool
|
|
||||||
skipVerify bool
|
|
||||||
tlsConfig *tls.Config
|
|
||||||
}
|
|
||||||
|
|
||||||
// DialReadTimeout specifies the timeout for reading a single command reply.
|
|
||||||
func DialReadTimeout(d time.Duration) DialOption {
|
|
||||||
return DialOption{func(do *dialOptions) {
|
|
||||||
do.readTimeout = d
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DialWriteTimeout specifies the timeout for writing a single command.
|
|
||||||
func DialWriteTimeout(d time.Duration) DialOption {
|
|
||||||
return DialOption{func(do *dialOptions) {
|
|
||||||
do.writeTimeout = d
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DialConnectTimeout specifies the timeout for connecting to the Redis server when
|
|
||||||
// no DialNetDial option is specified.
|
|
||||||
func DialConnectTimeout(d time.Duration) DialOption {
|
|
||||||
return DialOption{func(do *dialOptions) {
|
|
||||||
do.dialer.Timeout = d
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DialKeepAlive specifies the keep-alive period for TCP connections to the Redis server
|
|
||||||
// when no DialNetDial option is specified.
|
|
||||||
// If zero, keep-alives are not enabled. If no DialKeepAlive option is specified then
|
|
||||||
// the default of 5 minutes is used to ensure that half-closed TCP sessions are detected.
|
|
||||||
func DialKeepAlive(d time.Duration) DialOption {
|
|
||||||
return DialOption{func(do *dialOptions) {
|
|
||||||
do.dialer.KeepAlive = d
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DialNetDial specifies a custom dial function for creating TCP
|
|
||||||
// connections, otherwise a net.Dialer customized via the other options is used.
|
|
||||||
// DialNetDial overrides DialConnectTimeout and DialKeepAlive.
|
|
||||||
func DialNetDial(dial func(network, addr string) (net.Conn, error)) DialOption {
|
|
||||||
return DialOption{func(do *dialOptions) {
|
|
||||||
do.dial = dial
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DialDatabase specifies the database to select when dialing a connection.
|
|
||||||
func DialDatabase(db int) DialOption {
|
|
||||||
return DialOption{func(do *dialOptions) {
|
|
||||||
do.db = db
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DialPassword specifies the password to use when connecting to
|
|
||||||
// the Redis server.
|
|
||||||
func DialPassword(password string) DialOption {
|
|
||||||
return DialOption{func(do *dialOptions) {
|
|
||||||
do.password = password
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DialTLSConfig specifies the config to use when a TLS connection is dialed.
|
|
||||||
// Has no effect when not dialing a TLS connection.
|
|
||||||
func DialTLSConfig(c *tls.Config) DialOption {
|
|
||||||
return DialOption{func(do *dialOptions) {
|
|
||||||
do.tlsConfig = c
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DialTLSSkipVerify disables server name verification when connecting over
|
|
||||||
// TLS. Has no effect when not dialing a TLS connection.
|
|
||||||
func DialTLSSkipVerify(skip bool) DialOption {
|
|
||||||
return DialOption{func(do *dialOptions) {
|
|
||||||
do.skipVerify = skip
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DialUseTLS specifies whether TLS should be used when connecting to the
|
|
||||||
// server. This option is ignore by DialURL.
|
|
||||||
func DialUseTLS(useTLS bool) DialOption {
|
|
||||||
return DialOption{func(do *dialOptions) {
|
|
||||||
do.useTLS = useTLS
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dial connects to the Redis server at the given network and
|
|
||||||
// address using the specified options.
|
|
||||||
func Dial(network, address string, options ...DialOption) (Conn, error) {
|
|
||||||
do := dialOptions{
|
|
||||||
dialer: &net.Dialer{
|
|
||||||
KeepAlive: time.Minute * 5,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, option := range options {
|
|
||||||
option.f(&do)
|
|
||||||
}
|
|
||||||
if do.dial == nil {
|
|
||||||
do.dial = do.dialer.Dial
|
|
||||||
}
|
|
||||||
|
|
||||||
netConn, err := do.dial(network, address)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if do.useTLS {
|
|
||||||
tlsConfig := cloneTLSClientConfig(do.tlsConfig, do.skipVerify)
|
|
||||||
if tlsConfig.ServerName == "" {
|
|
||||||
host, _, err := net.SplitHostPort(address)
|
|
||||||
if err != nil {
|
|
||||||
netConn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
tlsConfig.ServerName = host
|
|
||||||
}
|
|
||||||
|
|
||||||
tlsConn := tls.Client(netConn, tlsConfig)
|
|
||||||
if err := tlsConn.Handshake(); err != nil {
|
|
||||||
netConn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
netConn = tlsConn
|
|
||||||
}
|
|
||||||
|
|
||||||
c := &conn{
|
|
||||||
conn: netConn,
|
|
||||||
bw: bufio.NewWriter(netConn),
|
|
||||||
br: bufio.NewReader(netConn),
|
|
||||||
readTimeout: do.readTimeout,
|
|
||||||
writeTimeout: do.writeTimeout,
|
|
||||||
}
|
|
||||||
|
|
||||||
if do.password != "" {
|
|
||||||
if _, err := c.Do("AUTH", do.password); err != nil {
|
|
||||||
netConn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if do.db != 0 {
|
|
||||||
if _, err := c.Do("SELECT", do.db); err != nil {
|
|
||||||
netConn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var pathDBRegexp = regexp.MustCompile(`/(\d*)\z`)
|
|
||||||
|
|
||||||
// DialURL connects to a Redis server at the given URL using the Redis
|
|
||||||
// URI scheme. URLs should follow the draft IANA specification for the
|
|
||||||
// scheme (https://www.iana.org/assignments/uri-schemes/prov/redis).
|
|
||||||
func DialURL(rawurl string, options ...DialOption) (Conn, error) {
|
|
||||||
u, err := url.Parse(rawurl)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.Scheme != "redis" && u.Scheme != "rediss" {
|
|
||||||
return nil, fmt.Errorf("invalid redis URL scheme: %s", u.Scheme)
|
|
||||||
}
|
|
||||||
|
|
||||||
// As per the IANA draft spec, the host defaults to localhost and
|
|
||||||
// the port defaults to 6379.
|
|
||||||
host, port, err := net.SplitHostPort(u.Host)
|
|
||||||
if err != nil {
|
|
||||||
// assume port is missing
|
|
||||||
host = u.Host
|
|
||||||
port = "6379"
|
|
||||||
}
|
|
||||||
if host == "" {
|
|
||||||
host = "localhost"
|
|
||||||
}
|
|
||||||
address := net.JoinHostPort(host, port)
|
|
||||||
|
|
||||||
if u.User != nil {
|
|
||||||
password, isSet := u.User.Password()
|
|
||||||
if isSet {
|
|
||||||
options = append(options, DialPassword(password))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match := pathDBRegexp.FindStringSubmatch(u.Path)
|
|
||||||
if len(match) == 2 {
|
|
||||||
db := 0
|
|
||||||
if len(match[1]) > 0 {
|
|
||||||
db, err = strconv.Atoi(match[1])
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid database: %s", u.Path[1:])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if db != 0 {
|
|
||||||
options = append(options, DialDatabase(db))
|
|
||||||
}
|
|
||||||
} else if u.Path != "" {
|
|
||||||
return nil, fmt.Errorf("invalid database: %s", u.Path[1:])
|
|
||||||
}
|
|
||||||
|
|
||||||
options = append(options, DialUseTLS(u.Scheme == "rediss"))
|
|
||||||
|
|
||||||
return Dial("tcp", address, options...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewConn returns a new Redigo connection for the given net connection.
|
|
||||||
func NewConn(netConn net.Conn, readTimeout, writeTimeout time.Duration) Conn {
|
|
||||||
return &conn{
|
|
||||||
conn: netConn,
|
|
||||||
bw: bufio.NewWriter(netConn),
|
|
||||||
br: bufio.NewReader(netConn),
|
|
||||||
readTimeout: readTimeout,
|
|
||||||
writeTimeout: writeTimeout,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) Close() error {
|
|
||||||
c.mu.Lock()
|
|
||||||
err := c.err
|
|
||||||
if c.err == nil {
|
|
||||||
c.err = errors.New("redigo: closed")
|
|
||||||
err = c.conn.Close()
|
|
||||||
}
|
|
||||||
c.mu.Unlock()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) fatal(err error) error {
|
|
||||||
c.mu.Lock()
|
|
||||||
if c.err == nil {
|
|
||||||
c.err = err
|
|
||||||
// Close connection to force errors on subsequent calls and to unblock
|
|
||||||
// other reader or writer.
|
|
||||||
c.conn.Close()
|
|
||||||
}
|
|
||||||
c.mu.Unlock()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) Err() error {
|
|
||||||
c.mu.Lock()
|
|
||||||
err := c.err
|
|
||||||
c.mu.Unlock()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) writeLen(prefix byte, n int) error {
|
|
||||||
c.lenScratch[len(c.lenScratch)-1] = '\n'
|
|
||||||
c.lenScratch[len(c.lenScratch)-2] = '\r'
|
|
||||||
i := len(c.lenScratch) - 3
|
|
||||||
for {
|
|
||||||
c.lenScratch[i] = byte('0' + n%10)
|
|
||||||
i -= 1
|
|
||||||
n = n / 10
|
|
||||||
if n == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.lenScratch[i] = prefix
|
|
||||||
_, err := c.bw.Write(c.lenScratch[i:])
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) writeString(s string) error {
|
|
||||||
c.writeLen('$', len(s))
|
|
||||||
c.bw.WriteString(s)
|
|
||||||
_, err := c.bw.WriteString("\r\n")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) writeBytes(p []byte) error {
|
|
||||||
c.writeLen('$', len(p))
|
|
||||||
c.bw.Write(p)
|
|
||||||
_, err := c.bw.WriteString("\r\n")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) writeInt64(n int64) error {
|
|
||||||
return c.writeBytes(strconv.AppendInt(c.numScratch[:0], n, 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) writeFloat64(n float64) error {
|
|
||||||
return c.writeBytes(strconv.AppendFloat(c.numScratch[:0], n, 'g', -1, 64))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) writeCommand(cmd string, args []interface{}) error {
|
|
||||||
c.writeLen('*', 1+len(args))
|
|
||||||
if err := c.writeString(cmd); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, arg := range args {
|
|
||||||
if err := c.writeArg(arg, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) writeArg(arg interface{}, argumentTypeOK bool) (err error) {
|
|
||||||
switch arg := arg.(type) {
|
|
||||||
case string:
|
|
||||||
return c.writeString(arg)
|
|
||||||
case []byte:
|
|
||||||
return c.writeBytes(arg)
|
|
||||||
case int:
|
|
||||||
return c.writeInt64(int64(arg))
|
|
||||||
case int64:
|
|
||||||
return c.writeInt64(arg)
|
|
||||||
case float64:
|
|
||||||
return c.writeFloat64(arg)
|
|
||||||
case bool:
|
|
||||||
if arg {
|
|
||||||
return c.writeString("1")
|
|
||||||
} else {
|
|
||||||
return c.writeString("0")
|
|
||||||
}
|
|
||||||
case nil:
|
|
||||||
return c.writeString("")
|
|
||||||
case Argument:
|
|
||||||
if argumentTypeOK {
|
|
||||||
return c.writeArg(arg.RedisArg(), false)
|
|
||||||
}
|
|
||||||
// See comment in default clause below.
|
|
||||||
var buf bytes.Buffer
|
|
||||||
fmt.Fprint(&buf, arg)
|
|
||||||
return c.writeBytes(buf.Bytes())
|
|
||||||
default:
|
|
||||||
// This default clause is intended to handle builtin numeric types.
|
|
||||||
// The function should return an error for other types, but this is not
|
|
||||||
// done for compatibility with previous versions of the package.
|
|
||||||
var buf bytes.Buffer
|
|
||||||
fmt.Fprint(&buf, arg)
|
|
||||||
return c.writeBytes(buf.Bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type protocolError string
|
|
||||||
|
|
||||||
func (pe protocolError) Error() string {
|
|
||||||
return fmt.Sprintf("redigo: %s (possible server error or unsupported concurrent read by application)", string(pe))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) readLine() ([]byte, error) {
|
|
||||||
p, err := c.br.ReadSlice('\n')
|
|
||||||
if err == bufio.ErrBufferFull {
|
|
||||||
return nil, protocolError("long response line")
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
i := len(p) - 2
|
|
||||||
if i < 0 || p[i] != '\r' {
|
|
||||||
return nil, protocolError("bad response line terminator")
|
|
||||||
}
|
|
||||||
return p[:i], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseLen parses bulk string and array lengths.
|
|
||||||
func parseLen(p []byte) (int, error) {
|
|
||||||
if len(p) == 0 {
|
|
||||||
return -1, protocolError("malformed length")
|
|
||||||
}
|
|
||||||
|
|
||||||
if p[0] == '-' && len(p) == 2 && p[1] == '1' {
|
|
||||||
// handle $-1 and $-1 null replies.
|
|
||||||
return -1, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var n int
|
|
||||||
for _, b := range p {
|
|
||||||
n *= 10
|
|
||||||
if b < '0' || b > '9' {
|
|
||||||
return -1, protocolError("illegal bytes in length")
|
|
||||||
}
|
|
||||||
n += int(b - '0')
|
|
||||||
}
|
|
||||||
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseInt parses an integer reply.
|
|
||||||
func parseInt(p []byte) (interface{}, error) {
|
|
||||||
if len(p) == 0 {
|
|
||||||
return 0, protocolError("malformed integer")
|
|
||||||
}
|
|
||||||
|
|
||||||
var negate bool
|
|
||||||
if p[0] == '-' {
|
|
||||||
negate = true
|
|
||||||
p = p[1:]
|
|
||||||
if len(p) == 0 {
|
|
||||||
return 0, protocolError("malformed integer")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var n int64
|
|
||||||
for _, b := range p {
|
|
||||||
n *= 10
|
|
||||||
if b < '0' || b > '9' {
|
|
||||||
return 0, protocolError("illegal bytes in length")
|
|
||||||
}
|
|
||||||
n += int64(b - '0')
|
|
||||||
}
|
|
||||||
|
|
||||||
if negate {
|
|
||||||
n = -n
|
|
||||||
}
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
okReply interface{} = "OK"
|
|
||||||
pongReply interface{} = "PONG"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *conn) readReply() (interface{}, error) {
|
|
||||||
line, err := c.readLine()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(line) == 0 {
|
|
||||||
return nil, protocolError("short response line")
|
|
||||||
}
|
|
||||||
switch line[0] {
|
|
||||||
case '+':
|
|
||||||
switch {
|
|
||||||
case len(line) == 3 && line[1] == 'O' && line[2] == 'K':
|
|
||||||
// Avoid allocation for frequent "+OK" response.
|
|
||||||
return okReply, nil
|
|
||||||
case len(line) == 5 && line[1] == 'P' && line[2] == 'O' && line[3] == 'N' && line[4] == 'G':
|
|
||||||
// Avoid allocation in PING command benchmarks :)
|
|
||||||
return pongReply, nil
|
|
||||||
default:
|
|
||||||
return string(line[1:]), nil
|
|
||||||
}
|
|
||||||
case '-':
|
|
||||||
return Error(string(line[1:])), nil
|
|
||||||
case ':':
|
|
||||||
return parseInt(line[1:])
|
|
||||||
case '$':
|
|
||||||
n, err := parseLen(line[1:])
|
|
||||||
if n < 0 || err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
p := make([]byte, n)
|
|
||||||
_, err = io.ReadFull(c.br, p)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if line, err := c.readLine(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if len(line) != 0 {
|
|
||||||
return nil, protocolError("bad bulk string format")
|
|
||||||
}
|
|
||||||
return p, nil
|
|
||||||
case '*':
|
|
||||||
n, err := parseLen(line[1:])
|
|
||||||
if n < 0 || err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
r := make([]interface{}, n)
|
|
||||||
for i := range r {
|
|
||||||
r[i], err = c.readReply()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
return nil, protocolError("unexpected response line")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) Send(cmd string, args ...interface{}) error {
|
|
||||||
c.mu.Lock()
|
|
||||||
c.pending += 1
|
|
||||||
c.mu.Unlock()
|
|
||||||
if c.writeTimeout != 0 {
|
|
||||||
c.conn.SetWriteDeadline(time.Now().Add(c.writeTimeout))
|
|
||||||
}
|
|
||||||
if err := c.writeCommand(cmd, args); err != nil {
|
|
||||||
return c.fatal(err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) Flush() error {
|
|
||||||
if c.writeTimeout != 0 {
|
|
||||||
c.conn.SetWriteDeadline(time.Now().Add(c.writeTimeout))
|
|
||||||
}
|
|
||||||
if err := c.bw.Flush(); err != nil {
|
|
||||||
return c.fatal(err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) Receive() (reply interface{}, err error) {
|
|
||||||
if c.readTimeout != 0 {
|
|
||||||
c.conn.SetReadDeadline(time.Now().Add(c.readTimeout))
|
|
||||||
}
|
|
||||||
if reply, err = c.readReply(); err != nil {
|
|
||||||
return nil, c.fatal(err)
|
|
||||||
}
|
|
||||||
// When using pub/sub, the number of receives can be greater than the
|
|
||||||
// number of sends. To enable normal use of the connection after
|
|
||||||
// unsubscribing from all channels, we do not decrement pending to a
|
|
||||||
// negative value.
|
|
||||||
//
|
|
||||||
// The pending field is decremented after the reply is read to handle the
|
|
||||||
// case where Receive is called before Send.
|
|
||||||
c.mu.Lock()
|
|
||||||
if c.pending > 0 {
|
|
||||||
c.pending -= 1
|
|
||||||
}
|
|
||||||
c.mu.Unlock()
|
|
||||||
if err, ok := reply.(Error); ok {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) Do(cmd string, args ...interface{}) (interface{}, error) {
|
|
||||||
c.mu.Lock()
|
|
||||||
pending := c.pending
|
|
||||||
c.pending = 0
|
|
||||||
c.mu.Unlock()
|
|
||||||
|
|
||||||
if cmd == "" && pending == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.writeTimeout != 0 {
|
|
||||||
c.conn.SetWriteDeadline(time.Now().Add(c.writeTimeout))
|
|
||||||
}
|
|
||||||
|
|
||||||
if cmd != "" {
|
|
||||||
if err := c.writeCommand(cmd, args); err != nil {
|
|
||||||
return nil, c.fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.bw.Flush(); err != nil {
|
|
||||||
return nil, c.fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.readTimeout != 0 {
|
|
||||||
c.conn.SetReadDeadline(time.Now().Add(c.readTimeout))
|
|
||||||
}
|
|
||||||
|
|
||||||
if cmd == "" {
|
|
||||||
reply := make([]interface{}, pending)
|
|
||||||
for i := range reply {
|
|
||||||
r, e := c.readReply()
|
|
||||||
if e != nil {
|
|
||||||
return nil, c.fatal(e)
|
|
||||||
}
|
|
||||||
reply[i] = r
|
|
||||||
}
|
|
||||||
return reply, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var reply interface{}
|
|
||||||
for i := 0; i <= pending; i++ {
|
|
||||||
var e error
|
|
||||||
if reply, e = c.readReply(); e != nil {
|
|
||||||
return nil, c.fatal(e)
|
|
||||||
}
|
|
||||||
if e, ok := reply.(Error); ok && err == nil {
|
|
||||||
err = e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return reply, err
|
|
||||||
}
|
|
177
vendor/github.com/garyburd/redigo/redis/doc.go
generated
vendored
177
vendor/github.com/garyburd/redigo/redis/doc.go
generated
vendored
|
@ -1,177 +0,0 @@
|
||||||
// Copyright 2012 Gary Burd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
// Package redis is a client for the Redis database.
|
|
||||||
//
|
|
||||||
// The Redigo FAQ (https://github.com/garyburd/redigo/wiki/FAQ) contains more
|
|
||||||
// documentation about this package.
|
|
||||||
//
|
|
||||||
// Connections
|
|
||||||
//
|
|
||||||
// The Conn interface is the primary interface for working with Redis.
|
|
||||||
// Applications create connections by calling the Dial, DialWithTimeout or
|
|
||||||
// NewConn functions. In the future, functions will be added for creating
|
|
||||||
// sharded and other types of connections.
|
|
||||||
//
|
|
||||||
// The application must call the connection Close method when the application
|
|
||||||
// is done with the connection.
|
|
||||||
//
|
|
||||||
// Executing Commands
|
|
||||||
//
|
|
||||||
// The Conn interface has a generic method for executing Redis commands:
|
|
||||||
//
|
|
||||||
// Do(commandName string, args ...interface{}) (reply interface{}, err error)
|
|
||||||
//
|
|
||||||
// The Redis command reference (http://redis.io/commands) lists the available
|
|
||||||
// commands. An example of using the Redis APPEND command is:
|
|
||||||
//
|
|
||||||
// n, err := conn.Do("APPEND", "key", "value")
|
|
||||||
//
|
|
||||||
// The Do method converts command arguments to bulk strings for transmission
|
|
||||||
// to the server as follows:
|
|
||||||
//
|
|
||||||
// Go Type Conversion
|
|
||||||
// []byte Sent as is
|
|
||||||
// string Sent as is
|
|
||||||
// int, int64 strconv.FormatInt(v)
|
|
||||||
// float64 strconv.FormatFloat(v, 'g', -1, 64)
|
|
||||||
// bool true -> "1", false -> "0"
|
|
||||||
// nil ""
|
|
||||||
// all other types fmt.Fprint(w, v)
|
|
||||||
//
|
|
||||||
// Redis command reply types are represented using the following Go types:
|
|
||||||
//
|
|
||||||
// Redis type Go type
|
|
||||||
// error redis.Error
|
|
||||||
// integer int64
|
|
||||||
// simple string string
|
|
||||||
// bulk string []byte or nil if value not present.
|
|
||||||
// array []interface{} or nil if value not present.
|
|
||||||
//
|
|
||||||
// Use type assertions or the reply helper functions to convert from
|
|
||||||
// interface{} to the specific Go type for the command result.
|
|
||||||
//
|
|
||||||
// Pipelining
|
|
||||||
//
|
|
||||||
// Connections support pipelining using the Send, Flush and Receive methods.
|
|
||||||
//
|
|
||||||
// Send(commandName string, args ...interface{}) error
|
|
||||||
// Flush() error
|
|
||||||
// Receive() (reply interface{}, err error)
|
|
||||||
//
|
|
||||||
// Send writes the command to the connection's output buffer. Flush flushes the
|
|
||||||
// connection's output buffer to the server. Receive reads a single reply from
|
|
||||||
// the server. The following example shows a simple pipeline.
|
|
||||||
//
|
|
||||||
// c.Send("SET", "foo", "bar")
|
|
||||||
// c.Send("GET", "foo")
|
|
||||||
// c.Flush()
|
|
||||||
// c.Receive() // reply from SET
|
|
||||||
// v, err = c.Receive() // reply from GET
|
|
||||||
//
|
|
||||||
// The Do method combines the functionality of the Send, Flush and Receive
|
|
||||||
// methods. The Do method starts by writing the command and flushing the output
|
|
||||||
// buffer. Next, the Do method receives all pending replies including the reply
|
|
||||||
// for the command just sent by Do. If any of the received replies is an error,
|
|
||||||
// then Do returns the error. If there are no errors, then Do returns the last
|
|
||||||
// reply. If the command argument to the Do method is "", then the Do method
|
|
||||||
// will flush the output buffer and receive pending replies without sending a
|
|
||||||
// command.
|
|
||||||
//
|
|
||||||
// Use the Send and Do methods to implement pipelined transactions.
|
|
||||||
//
|
|
||||||
// c.Send("MULTI")
|
|
||||||
// c.Send("INCR", "foo")
|
|
||||||
// c.Send("INCR", "bar")
|
|
||||||
// r, err := c.Do("EXEC")
|
|
||||||
// fmt.Println(r) // prints [1, 1]
|
|
||||||
//
|
|
||||||
// Concurrency
|
|
||||||
//
|
|
||||||
// Connections support one concurrent caller to the Receive method and one
|
|
||||||
// concurrent caller to the Send and Flush methods. No other concurrency is
|
|
||||||
// supported including concurrent calls to the Do method.
|
|
||||||
//
|
|
||||||
// For full concurrent access to Redis, use the thread-safe Pool to get, use
|
|
||||||
// and release a connection from within a goroutine. Connections returned from
|
|
||||||
// a Pool have the concurrency restrictions described in the previous
|
|
||||||
// paragraph.
|
|
||||||
//
|
|
||||||
// Publish and Subscribe
|
|
||||||
//
|
|
||||||
// Use the Send, Flush and Receive methods to implement Pub/Sub subscribers.
|
|
||||||
//
|
|
||||||
// c.Send("SUBSCRIBE", "example")
|
|
||||||
// c.Flush()
|
|
||||||
// for {
|
|
||||||
// reply, err := c.Receive()
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// // process pushed message
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// The PubSubConn type wraps a Conn with convenience methods for implementing
|
|
||||||
// subscribers. The Subscribe, PSubscribe, Unsubscribe and PUnsubscribe methods
|
|
||||||
// send and flush a subscription management command. The receive method
|
|
||||||
// converts a pushed message to convenient types for use in a type switch.
|
|
||||||
//
|
|
||||||
// psc := redis.PubSubConn{Conn: c}
|
|
||||||
// psc.Subscribe("example")
|
|
||||||
// for {
|
|
||||||
// switch v := psc.Receive().(type) {
|
|
||||||
// case redis.Message:
|
|
||||||
// fmt.Printf("%s: message: %s\n", v.Channel, v.Data)
|
|
||||||
// case redis.Subscription:
|
|
||||||
// fmt.Printf("%s: %s %d\n", v.Channel, v.Kind, v.Count)
|
|
||||||
// case error:
|
|
||||||
// return v
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Reply Helpers
|
|
||||||
//
|
|
||||||
// The Bool, Int, Bytes, String, Strings and Values functions convert a reply
|
|
||||||
// to a value of a specific type. To allow convenient wrapping of calls to the
|
|
||||||
// connection Do and Receive methods, the functions take a second argument of
|
|
||||||
// type error. If the error is non-nil, then the helper function returns the
|
|
||||||
// error. If the error is nil, the function converts the reply to the specified
|
|
||||||
// type:
|
|
||||||
//
|
|
||||||
// exists, err := redis.Bool(c.Do("EXISTS", "foo"))
|
|
||||||
// if err != nil {
|
|
||||||
// // handle error return from c.Do or type conversion error.
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// The Scan function converts elements of a array reply to Go types:
|
|
||||||
//
|
|
||||||
// var value1 int
|
|
||||||
// var value2 string
|
|
||||||
// reply, err := redis.Values(c.Do("MGET", "key1", "key2"))
|
|
||||||
// if err != nil {
|
|
||||||
// // handle error
|
|
||||||
// }
|
|
||||||
// if _, err := redis.Scan(reply, &value1, &value2); err != nil {
|
|
||||||
// // handle error
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Errors
|
|
||||||
//
|
|
||||||
// Connection methods return error replies from the server as type redis.Error.
|
|
||||||
//
|
|
||||||
// Call the connection Err() method to determine if the connection encountered
|
|
||||||
// non-recoverable error such as a network error or protocol parsing error. If
|
|
||||||
// Err() returns a non-nil value, then the connection is not usable and should
|
|
||||||
// be closed.
|
|
||||||
package redis // import "github.com/garyburd/redigo/redis"
|
|
33
vendor/github.com/garyburd/redigo/redis/go17.go
generated
vendored
33
vendor/github.com/garyburd/redigo/redis/go17.go
generated
vendored
|
@ -1,33 +0,0 @@
|
||||||
// +build go1.7
|
|
||||||
|
|
||||||
package redis
|
|
||||||
|
|
||||||
import "crypto/tls"
|
|
||||||
|
|
||||||
// similar cloneTLSClientConfig in the stdlib, but also honor skipVerify for the nil case
|
|
||||||
func cloneTLSClientConfig(cfg *tls.Config, skipVerify bool) *tls.Config {
|
|
||||||
if cfg == nil {
|
|
||||||
return &tls.Config{InsecureSkipVerify: skipVerify}
|
|
||||||
}
|
|
||||||
return &tls.Config{
|
|
||||||
Rand: cfg.Rand,
|
|
||||||
Time: cfg.Time,
|
|
||||||
Certificates: cfg.Certificates,
|
|
||||||
NameToCertificate: cfg.NameToCertificate,
|
|
||||||
GetCertificate: cfg.GetCertificate,
|
|
||||||
RootCAs: cfg.RootCAs,
|
|
||||||
NextProtos: cfg.NextProtos,
|
|
||||||
ServerName: cfg.ServerName,
|
|
||||||
ClientAuth: cfg.ClientAuth,
|
|
||||||
ClientCAs: cfg.ClientCAs,
|
|
||||||
InsecureSkipVerify: cfg.InsecureSkipVerify,
|
|
||||||
CipherSuites: cfg.CipherSuites,
|
|
||||||
PreferServerCipherSuites: cfg.PreferServerCipherSuites,
|
|
||||||
ClientSessionCache: cfg.ClientSessionCache,
|
|
||||||
MinVersion: cfg.MinVersion,
|
|
||||||
MaxVersion: cfg.MaxVersion,
|
|
||||||
CurvePreferences: cfg.CurvePreferences,
|
|
||||||
DynamicRecordSizingDisabled: cfg.DynamicRecordSizingDisabled,
|
|
||||||
Renegotiation: cfg.Renegotiation,
|
|
||||||
}
|
|
||||||
}
|
|
117
vendor/github.com/garyburd/redigo/redis/log.go
generated
vendored
117
vendor/github.com/garyburd/redigo/redis/log.go
generated
vendored
|
@ -1,117 +0,0 @@
|
||||||
// Copyright 2012 Gary Burd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewLoggingConn returns a logging wrapper around a connection.
|
|
||||||
func NewLoggingConn(conn Conn, logger *log.Logger, prefix string) Conn {
|
|
||||||
if prefix != "" {
|
|
||||||
prefix = prefix + "."
|
|
||||||
}
|
|
||||||
return &loggingConn{conn, logger, prefix}
|
|
||||||
}
|
|
||||||
|
|
||||||
type loggingConn struct {
|
|
||||||
Conn
|
|
||||||
logger *log.Logger
|
|
||||||
prefix string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *loggingConn) Close() error {
|
|
||||||
err := c.Conn.Close()
|
|
||||||
var buf bytes.Buffer
|
|
||||||
fmt.Fprintf(&buf, "%sClose() -> (%v)", c.prefix, err)
|
|
||||||
c.logger.Output(2, buf.String())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *loggingConn) printValue(buf *bytes.Buffer, v interface{}) {
|
|
||||||
const chop = 32
|
|
||||||
switch v := v.(type) {
|
|
||||||
case []byte:
|
|
||||||
if len(v) > chop {
|
|
||||||
fmt.Fprintf(buf, "%q...", v[:chop])
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(buf, "%q", v)
|
|
||||||
}
|
|
||||||
case string:
|
|
||||||
if len(v) > chop {
|
|
||||||
fmt.Fprintf(buf, "%q...", v[:chop])
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(buf, "%q", v)
|
|
||||||
}
|
|
||||||
case []interface{}:
|
|
||||||
if len(v) == 0 {
|
|
||||||
buf.WriteString("[]")
|
|
||||||
} else {
|
|
||||||
sep := "["
|
|
||||||
fin := "]"
|
|
||||||
if len(v) > chop {
|
|
||||||
v = v[:chop]
|
|
||||||
fin = "...]"
|
|
||||||
}
|
|
||||||
for _, vv := range v {
|
|
||||||
buf.WriteString(sep)
|
|
||||||
c.printValue(buf, vv)
|
|
||||||
sep = ", "
|
|
||||||
}
|
|
||||||
buf.WriteString(fin)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
fmt.Fprint(buf, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *loggingConn) print(method, commandName string, args []interface{}, reply interface{}, err error) {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
fmt.Fprintf(&buf, "%s%s(", c.prefix, method)
|
|
||||||
if method != "Receive" {
|
|
||||||
buf.WriteString(commandName)
|
|
||||||
for _, arg := range args {
|
|
||||||
buf.WriteString(", ")
|
|
||||||
c.printValue(&buf, arg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buf.WriteString(") -> (")
|
|
||||||
if method != "Send" {
|
|
||||||
c.printValue(&buf, reply)
|
|
||||||
buf.WriteString(", ")
|
|
||||||
}
|
|
||||||
fmt.Fprintf(&buf, "%v)", err)
|
|
||||||
c.logger.Output(3, buf.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *loggingConn) Do(commandName string, args ...interface{}) (interface{}, error) {
|
|
||||||
reply, err := c.Conn.Do(commandName, args...)
|
|
||||||
c.print("Do", commandName, args, reply, err)
|
|
||||||
return reply, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *loggingConn) Send(commandName string, args ...interface{}) error {
|
|
||||||
err := c.Conn.Send(commandName, args...)
|
|
||||||
c.print("Send", commandName, args, nil, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *loggingConn) Receive() (interface{}, error) {
|
|
||||||
reply, err := c.Conn.Receive()
|
|
||||||
c.print("Receive", "", nil, reply, err)
|
|
||||||
return reply, err
|
|
||||||
}
|
|
442
vendor/github.com/garyburd/redigo/redis/pool.go
generated
vendored
442
vendor/github.com/garyburd/redigo/redis/pool.go
generated
vendored
|
@ -1,442 +0,0 @@
|
||||||
// Copyright 2012 Gary Burd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"container/list"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha1"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/garyburd/redigo/internal"
|
|
||||||
)
|
|
||||||
|
|
||||||
var nowFunc = time.Now // for testing
|
|
||||||
|
|
||||||
// ErrPoolExhausted is returned from a pool connection method (Do, Send,
|
|
||||||
// Receive, Flush, Err) when the maximum number of database connections in the
|
|
||||||
// pool has been reached.
|
|
||||||
var ErrPoolExhausted = errors.New("redigo: connection pool exhausted")
|
|
||||||
|
|
||||||
var (
|
|
||||||
errPoolClosed = errors.New("redigo: connection pool closed")
|
|
||||||
errConnClosed = errors.New("redigo: connection closed")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Pool maintains a pool of connections. The application calls the Get method
|
|
||||||
// to get a connection from the pool and the connection's Close method to
|
|
||||||
// return the connection's resources to the pool.
|
|
||||||
//
|
|
||||||
// The following example shows how to use a pool in a web application. The
|
|
||||||
// application creates a pool at application startup and makes it available to
|
|
||||||
// request handlers using a package level variable. The pool configuration used
|
|
||||||
// here is an example, not a recommendation.
|
|
||||||
//
|
|
||||||
// func newPool(addr string) *redis.Pool {
|
|
||||||
// return &redis.Pool{
|
|
||||||
// MaxIdle: 3,
|
|
||||||
// IdleTimeout: 240 * time.Second,
|
|
||||||
// Dial: func () (redis.Conn, error) { return redis.Dial("tcp", addr) },
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// var (
|
|
||||||
// pool *redis.Pool
|
|
||||||
// redisServer = flag.String("redisServer", ":6379", "")
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// func main() {
|
|
||||||
// flag.Parse()
|
|
||||||
// pool = newPool(*redisServer)
|
|
||||||
// ...
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// A request handler gets a connection from the pool and closes the connection
|
|
||||||
// when the handler is done:
|
|
||||||
//
|
|
||||||
// func serveHome(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// conn := pool.Get()
|
|
||||||
// defer conn.Close()
|
|
||||||
// ...
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Use the Dial function to authenticate connections with the AUTH command or
|
|
||||||
// select a database with the SELECT command:
|
|
||||||
//
|
|
||||||
// pool := &redis.Pool{
|
|
||||||
// // Other pool configuration not shown in this example.
|
|
||||||
// Dial: func () (redis.Conn, error) {
|
|
||||||
// c, err := redis.Dial("tcp", server)
|
|
||||||
// if err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
// if _, err := c.Do("AUTH", password); err != nil {
|
|
||||||
// c.Close()
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
// if _, err := c.Do("SELECT", db); err != nil {
|
|
||||||
// c.Close()
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
// return c, nil
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Use the TestOnBorrow function to check the health of an idle connection
|
|
||||||
// before the connection is returned to the application. This example PINGs
|
|
||||||
// connections that have been idle more than a minute:
|
|
||||||
//
|
|
||||||
// pool := &redis.Pool{
|
|
||||||
// // Other pool configuration not shown in this example.
|
|
||||||
// TestOnBorrow: func(c redis.Conn, t time.Time) error {
|
|
||||||
// if time.Since(t) < time.Minute {
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
// _, err := c.Do("PING")
|
|
||||||
// return err
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
type Pool struct {
|
|
||||||
// Dial is an application supplied function for creating and configuring a
|
|
||||||
// connection.
|
|
||||||
//
|
|
||||||
// The connection returned from Dial must not be in a special state
|
|
||||||
// (subscribed to pubsub channel, transaction started, ...).
|
|
||||||
Dial func() (Conn, error)
|
|
||||||
|
|
||||||
// TestOnBorrow is an optional application supplied function for checking
|
|
||||||
// the health of an idle connection before the connection is used again by
|
|
||||||
// the application. Argument t is the time that the connection was returned
|
|
||||||
// to the pool. If the function returns an error, then the connection is
|
|
||||||
// closed.
|
|
||||||
TestOnBorrow func(c Conn, t time.Time) error
|
|
||||||
|
|
||||||
// Maximum number of idle connections in the pool.
|
|
||||||
MaxIdle int
|
|
||||||
|
|
||||||
// Maximum number of connections allocated by the pool at a given time.
|
|
||||||
// When zero, there is no limit on the number of connections in the pool.
|
|
||||||
MaxActive int
|
|
||||||
|
|
||||||
// Close connections after remaining idle for this duration. If the value
|
|
||||||
// is zero, then idle connections are not closed. Applications should set
|
|
||||||
// the timeout to a value less than the server's timeout.
|
|
||||||
IdleTimeout time.Duration
|
|
||||||
|
|
||||||
// If Wait is true and the pool is at the MaxActive limit, then Get() waits
|
|
||||||
// for a connection to be returned to the pool before returning.
|
|
||||||
Wait bool
|
|
||||||
|
|
||||||
// mu protects fields defined below.
|
|
||||||
mu sync.Mutex
|
|
||||||
cond *sync.Cond
|
|
||||||
closed bool
|
|
||||||
active int
|
|
||||||
|
|
||||||
// Stack of idleConn with most recently used at the front.
|
|
||||||
idle list.List
|
|
||||||
}
|
|
||||||
|
|
||||||
type idleConn struct {
|
|
||||||
c Conn
|
|
||||||
t time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPool creates a new pool.
|
|
||||||
//
|
|
||||||
// Deprecated: Initialize the Pool directory as shown in the example.
|
|
||||||
func NewPool(newFn func() (Conn, error), maxIdle int) *Pool {
|
|
||||||
return &Pool{Dial: newFn, MaxIdle: maxIdle}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get gets a connection. The application must close the returned connection.
|
|
||||||
// This method always returns a valid connection so that applications can defer
|
|
||||||
// error handling to the first use of the connection. If there is an error
|
|
||||||
// getting an underlying connection, then the connection Err, Do, Send, Flush
|
|
||||||
// and Receive methods return that error.
|
|
||||||
func (p *Pool) Get() Conn {
|
|
||||||
c, err := p.get()
|
|
||||||
if err != nil {
|
|
||||||
return errorConnection{err}
|
|
||||||
}
|
|
||||||
return &pooledConnection{p: p, c: c}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PoolStats contains pool statistics.
|
|
||||||
type PoolStats struct {
|
|
||||||
// ActiveCount is the number of connections in the pool. The count includes idle connections and connections in use.
|
|
||||||
ActiveCount int
|
|
||||||
// IdleCount is the number of idle connections in the pool.
|
|
||||||
IdleCount int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stats returns pool's statistics.
|
|
||||||
func (p *Pool) Stats() PoolStats {
|
|
||||||
p.mu.Lock()
|
|
||||||
stats := PoolStats{
|
|
||||||
ActiveCount: p.active,
|
|
||||||
IdleCount: p.idle.Len(),
|
|
||||||
}
|
|
||||||
p.mu.Unlock()
|
|
||||||
|
|
||||||
return stats
|
|
||||||
}
|
|
||||||
|
|
||||||
// ActiveCount returns the number of connections in the pool. The count includes idle connections and connections in use.
|
|
||||||
func (p *Pool) ActiveCount() int {
|
|
||||||
p.mu.Lock()
|
|
||||||
active := p.active
|
|
||||||
p.mu.Unlock()
|
|
||||||
return active
|
|
||||||
}
|
|
||||||
|
|
||||||
// IdleCount returns the number of idle connections in the pool.
|
|
||||||
func (p *Pool) IdleCount() int {
|
|
||||||
p.mu.Lock()
|
|
||||||
idle := p.idle.Len()
|
|
||||||
p.mu.Unlock()
|
|
||||||
return idle
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close releases the resources used by the pool.
|
|
||||||
func (p *Pool) Close() error {
|
|
||||||
p.mu.Lock()
|
|
||||||
idle := p.idle
|
|
||||||
p.idle.Init()
|
|
||||||
p.closed = true
|
|
||||||
p.active -= idle.Len()
|
|
||||||
if p.cond != nil {
|
|
||||||
p.cond.Broadcast()
|
|
||||||
}
|
|
||||||
p.mu.Unlock()
|
|
||||||
for e := idle.Front(); e != nil; e = e.Next() {
|
|
||||||
e.Value.(idleConn).c.Close()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// release decrements the active count and signals waiters. The caller must
|
|
||||||
// hold p.mu during the call.
|
|
||||||
func (p *Pool) release() {
|
|
||||||
p.active -= 1
|
|
||||||
if p.cond != nil {
|
|
||||||
p.cond.Signal()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get prunes stale connections and returns a connection from the idle list or
|
|
||||||
// creates a new connection.
|
|
||||||
func (p *Pool) get() (Conn, error) {
|
|
||||||
p.mu.Lock()
|
|
||||||
|
|
||||||
// Prune stale connections.
|
|
||||||
|
|
||||||
if timeout := p.IdleTimeout; timeout > 0 {
|
|
||||||
for i, n := 0, p.idle.Len(); i < n; i++ {
|
|
||||||
e := p.idle.Back()
|
|
||||||
if e == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
ic := e.Value.(idleConn)
|
|
||||||
if ic.t.Add(timeout).After(nowFunc()) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
p.idle.Remove(e)
|
|
||||||
p.release()
|
|
||||||
p.mu.Unlock()
|
|
||||||
ic.c.Close()
|
|
||||||
p.mu.Lock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
// Get idle connection.
|
|
||||||
|
|
||||||
for i, n := 0, p.idle.Len(); i < n; i++ {
|
|
||||||
e := p.idle.Front()
|
|
||||||
if e == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
ic := e.Value.(idleConn)
|
|
||||||
p.idle.Remove(e)
|
|
||||||
test := p.TestOnBorrow
|
|
||||||
p.mu.Unlock()
|
|
||||||
if test == nil || test(ic.c, ic.t) == nil {
|
|
||||||
return ic.c, nil
|
|
||||||
}
|
|
||||||
ic.c.Close()
|
|
||||||
p.mu.Lock()
|
|
||||||
p.release()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for pool closed before dialing a new connection.
|
|
||||||
|
|
||||||
if p.closed {
|
|
||||||
p.mu.Unlock()
|
|
||||||
return nil, errors.New("redigo: get on closed pool")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dial new connection if under limit.
|
|
||||||
|
|
||||||
if p.MaxActive == 0 || p.active < p.MaxActive {
|
|
||||||
dial := p.Dial
|
|
||||||
p.active += 1
|
|
||||||
p.mu.Unlock()
|
|
||||||
c, err := dial()
|
|
||||||
if err != nil {
|
|
||||||
p.mu.Lock()
|
|
||||||
p.release()
|
|
||||||
p.mu.Unlock()
|
|
||||||
c = nil
|
|
||||||
}
|
|
||||||
return c, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !p.Wait {
|
|
||||||
p.mu.Unlock()
|
|
||||||
return nil, ErrPoolExhausted
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.cond == nil {
|
|
||||||
p.cond = sync.NewCond(&p.mu)
|
|
||||||
}
|
|
||||||
p.cond.Wait()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Pool) put(c Conn, forceClose bool) error {
|
|
||||||
err := c.Err()
|
|
||||||
p.mu.Lock()
|
|
||||||
if !p.closed && err == nil && !forceClose {
|
|
||||||
p.idle.PushFront(idleConn{t: nowFunc(), c: c})
|
|
||||||
if p.idle.Len() > p.MaxIdle {
|
|
||||||
c = p.idle.Remove(p.idle.Back()).(idleConn).c
|
|
||||||
} else {
|
|
||||||
c = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if c == nil {
|
|
||||||
if p.cond != nil {
|
|
||||||
p.cond.Signal()
|
|
||||||
}
|
|
||||||
p.mu.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
p.release()
|
|
||||||
p.mu.Unlock()
|
|
||||||
return c.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
type pooledConnection struct {
|
|
||||||
p *Pool
|
|
||||||
c Conn
|
|
||||||
state int
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
sentinel []byte
|
|
||||||
sentinelOnce sync.Once
|
|
||||||
)
|
|
||||||
|
|
||||||
func initSentinel() {
|
|
||||||
p := make([]byte, 64)
|
|
||||||
if _, err := rand.Read(p); err == nil {
|
|
||||||
sentinel = p
|
|
||||||
} else {
|
|
||||||
h := sha1.New()
|
|
||||||
io.WriteString(h, "Oops, rand failed. Use time instead.")
|
|
||||||
io.WriteString(h, strconv.FormatInt(time.Now().UnixNano(), 10))
|
|
||||||
sentinel = h.Sum(nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pc *pooledConnection) Close() error {
|
|
||||||
c := pc.c
|
|
||||||
if _, ok := c.(errorConnection); ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
pc.c = errorConnection{errConnClosed}
|
|
||||||
|
|
||||||
if pc.state&internal.MultiState != 0 {
|
|
||||||
c.Send("DISCARD")
|
|
||||||
pc.state &^= (internal.MultiState | internal.WatchState)
|
|
||||||
} else if pc.state&internal.WatchState != 0 {
|
|
||||||
c.Send("UNWATCH")
|
|
||||||
pc.state &^= internal.WatchState
|
|
||||||
}
|
|
||||||
if pc.state&internal.SubscribeState != 0 {
|
|
||||||
c.Send("UNSUBSCRIBE")
|
|
||||||
c.Send("PUNSUBSCRIBE")
|
|
||||||
// To detect the end of the message stream, ask the server to echo
|
|
||||||
// a sentinel value and read until we see that value.
|
|
||||||
sentinelOnce.Do(initSentinel)
|
|
||||||
c.Send("ECHO", sentinel)
|
|
||||||
c.Flush()
|
|
||||||
for {
|
|
||||||
p, err := c.Receive()
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if p, ok := p.([]byte); ok && bytes.Equal(p, sentinel) {
|
|
||||||
pc.state &^= internal.SubscribeState
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.Do("")
|
|
||||||
pc.p.put(c, pc.state != 0)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pc *pooledConnection) Err() error {
|
|
||||||
return pc.c.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pc *pooledConnection) Do(commandName string, args ...interface{}) (reply interface{}, err error) {
|
|
||||||
ci := internal.LookupCommandInfo(commandName)
|
|
||||||
pc.state = (pc.state | ci.Set) &^ ci.Clear
|
|
||||||
return pc.c.Do(commandName, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pc *pooledConnection) Send(commandName string, args ...interface{}) error {
|
|
||||||
ci := internal.LookupCommandInfo(commandName)
|
|
||||||
pc.state = (pc.state | ci.Set) &^ ci.Clear
|
|
||||||
return pc.c.Send(commandName, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pc *pooledConnection) Flush() error {
|
|
||||||
return pc.c.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pc *pooledConnection) Receive() (reply interface{}, err error) {
|
|
||||||
return pc.c.Receive()
|
|
||||||
}
|
|
||||||
|
|
||||||
type errorConnection struct{ err error }
|
|
||||||
|
|
||||||
func (ec errorConnection) Do(string, ...interface{}) (interface{}, error) { return nil, ec.err }
|
|
||||||
func (ec errorConnection) Send(string, ...interface{}) error { return ec.err }
|
|
||||||
func (ec errorConnection) Err() error { return ec.err }
|
|
||||||
func (ec errorConnection) Close() error { return ec.err }
|
|
||||||
func (ec errorConnection) Flush() error { return ec.err }
|
|
||||||
func (ec errorConnection) Receive() (interface{}, error) { return nil, ec.err }
|
|
31
vendor/github.com/garyburd/redigo/redis/pre_go17.go
generated
vendored
31
vendor/github.com/garyburd/redigo/redis/pre_go17.go
generated
vendored
|
@ -1,31 +0,0 @@
|
||||||
// +build !go1.7
|
|
||||||
|
|
||||||
package redis
|
|
||||||
|
|
||||||
import "crypto/tls"
|
|
||||||
|
|
||||||
// similar cloneTLSClientConfig in the stdlib, but also honor skipVerify for the nil case
|
|
||||||
func cloneTLSClientConfig(cfg *tls.Config, skipVerify bool) *tls.Config {
|
|
||||||
if cfg == nil {
|
|
||||||
return &tls.Config{InsecureSkipVerify: skipVerify}
|
|
||||||
}
|
|
||||||
return &tls.Config{
|
|
||||||
Rand: cfg.Rand,
|
|
||||||
Time: cfg.Time,
|
|
||||||
Certificates: cfg.Certificates,
|
|
||||||
NameToCertificate: cfg.NameToCertificate,
|
|
||||||
GetCertificate: cfg.GetCertificate,
|
|
||||||
RootCAs: cfg.RootCAs,
|
|
||||||
NextProtos: cfg.NextProtos,
|
|
||||||
ServerName: cfg.ServerName,
|
|
||||||
ClientAuth: cfg.ClientAuth,
|
|
||||||
ClientCAs: cfg.ClientCAs,
|
|
||||||
InsecureSkipVerify: cfg.InsecureSkipVerify,
|
|
||||||
CipherSuites: cfg.CipherSuites,
|
|
||||||
PreferServerCipherSuites: cfg.PreferServerCipherSuites,
|
|
||||||
ClientSessionCache: cfg.ClientSessionCache,
|
|
||||||
MinVersion: cfg.MinVersion,
|
|
||||||
MaxVersion: cfg.MaxVersion,
|
|
||||||
CurvePreferences: cfg.CurvePreferences,
|
|
||||||
}
|
|
||||||
}
|
|
144
vendor/github.com/garyburd/redigo/redis/pubsub.go
generated
vendored
144
vendor/github.com/garyburd/redigo/redis/pubsub.go
generated
vendored
|
@ -1,144 +0,0 @@
|
||||||
// Copyright 2012 Gary Burd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package redis
|
|
||||||
|
|
||||||
import "errors"
|
|
||||||
|
|
||||||
// Subscription represents a subscribe or unsubscribe notification.
|
|
||||||
type Subscription struct {
|
|
||||||
// Kind is "subscribe", "unsubscribe", "psubscribe" or "punsubscribe"
|
|
||||||
Kind string
|
|
||||||
|
|
||||||
// The channel that was changed.
|
|
||||||
Channel string
|
|
||||||
|
|
||||||
// The current number of subscriptions for connection.
|
|
||||||
Count int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Message represents a message notification.
|
|
||||||
type Message struct {
|
|
||||||
// The originating channel.
|
|
||||||
Channel string
|
|
||||||
|
|
||||||
// The message data.
|
|
||||||
Data []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// PMessage represents a pmessage notification.
|
|
||||||
type PMessage struct {
|
|
||||||
// The matched pattern.
|
|
||||||
Pattern string
|
|
||||||
|
|
||||||
// The originating channel.
|
|
||||||
Channel string
|
|
||||||
|
|
||||||
// The message data.
|
|
||||||
Data []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pong represents a pubsub pong notification.
|
|
||||||
type Pong struct {
|
|
||||||
Data string
|
|
||||||
}
|
|
||||||
|
|
||||||
// PubSubConn wraps a Conn with convenience methods for subscribers.
|
|
||||||
type PubSubConn struct {
|
|
||||||
Conn Conn
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes the connection.
|
|
||||||
func (c PubSubConn) Close() error {
|
|
||||||
return c.Conn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe subscribes the connection to the specified channels.
|
|
||||||
func (c PubSubConn) Subscribe(channel ...interface{}) error {
|
|
||||||
c.Conn.Send("SUBSCRIBE", channel...)
|
|
||||||
return c.Conn.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
// PSubscribe subscribes the connection to the given patterns.
|
|
||||||
func (c PubSubConn) PSubscribe(channel ...interface{}) error {
|
|
||||||
c.Conn.Send("PSUBSCRIBE", channel...)
|
|
||||||
return c.Conn.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unsubscribe unsubscribes the connection from the given channels, or from all
|
|
||||||
// of them if none is given.
|
|
||||||
func (c PubSubConn) Unsubscribe(channel ...interface{}) error {
|
|
||||||
c.Conn.Send("UNSUBSCRIBE", channel...)
|
|
||||||
return c.Conn.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
// PUnsubscribe unsubscribes the connection from the given patterns, or from all
|
|
||||||
// of them if none is given.
|
|
||||||
func (c PubSubConn) PUnsubscribe(channel ...interface{}) error {
|
|
||||||
c.Conn.Send("PUNSUBSCRIBE", channel...)
|
|
||||||
return c.Conn.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ping sends a PING to the server with the specified data.
|
|
||||||
//
|
|
||||||
// The connection must be subscribed to at least one channel or pattern when
|
|
||||||
// calling this method.
|
|
||||||
func (c PubSubConn) Ping(data string) error {
|
|
||||||
c.Conn.Send("PING", data)
|
|
||||||
return c.Conn.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Receive returns a pushed message as a Subscription, Message, PMessage, Pong
|
|
||||||
// or error. The return value is intended to be used directly in a type switch
|
|
||||||
// as illustrated in the PubSubConn example.
|
|
||||||
func (c PubSubConn) Receive() interface{} {
|
|
||||||
reply, err := Values(c.Conn.Receive())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var kind string
|
|
||||||
reply, err = Scan(reply, &kind)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch kind {
|
|
||||||
case "message":
|
|
||||||
var m Message
|
|
||||||
if _, err := Scan(reply, &m.Channel, &m.Data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
case "pmessage":
|
|
||||||
var pm PMessage
|
|
||||||
if _, err := Scan(reply, &pm.Pattern, &pm.Channel, &pm.Data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return pm
|
|
||||||
case "subscribe", "psubscribe", "unsubscribe", "punsubscribe":
|
|
||||||
s := Subscription{Kind: kind}
|
|
||||||
if _, err := Scan(reply, &s.Channel, &s.Count); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
case "pong":
|
|
||||||
var p Pong
|
|
||||||
if _, err := Scan(reply, &p.Data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
return errors.New("redigo: unknown pubsub notification")
|
|
||||||
}
|
|
61
vendor/github.com/garyburd/redigo/redis/redis.go
generated
vendored
61
vendor/github.com/garyburd/redigo/redis/redis.go
generated
vendored
|
@ -1,61 +0,0 @@
|
||||||
// Copyright 2012 Gary Burd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package redis
|
|
||||||
|
|
||||||
// Error represents an error returned in a command reply.
|
|
||||||
type Error string
|
|
||||||
|
|
||||||
func (err Error) Error() string { return string(err) }
|
|
||||||
|
|
||||||
// Conn represents a connection to a Redis server.
|
|
||||||
type Conn interface {
|
|
||||||
// Close closes the connection.
|
|
||||||
Close() error
|
|
||||||
|
|
||||||
// Err returns a non-nil value when the connection is not usable.
|
|
||||||
Err() error
|
|
||||||
|
|
||||||
// Do sends a command to the server and returns the received reply.
|
|
||||||
Do(commandName string, args ...interface{}) (reply interface{}, err error)
|
|
||||||
|
|
||||||
// Send writes the command to the client's output buffer.
|
|
||||||
Send(commandName string, args ...interface{}) error
|
|
||||||
|
|
||||||
// Flush flushes the output buffer to the Redis server.
|
|
||||||
Flush() error
|
|
||||||
|
|
||||||
// Receive receives a single reply from the Redis server
|
|
||||||
Receive() (reply interface{}, err error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Argument is the interface implemented by an object which wants to control how
|
|
||||||
// the object is converted to Redis bulk strings.
|
|
||||||
type Argument interface {
|
|
||||||
// RedisArg returns a value to be encoded as a bulk string per the
|
|
||||||
// conversions listed in the section 'Executing Commands'.
|
|
||||||
// Implementations should typically return a []byte or string.
|
|
||||||
RedisArg() interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scanner is implemented by an object which wants to control its value is
|
|
||||||
// interpreted when read from Redis.
|
|
||||||
type Scanner interface {
|
|
||||||
// RedisScan assigns a value from a Redis value. The argument src is one of
|
|
||||||
// the reply types listed in the section `Executing Commands`.
|
|
||||||
//
|
|
||||||
// An error should be returned if the value cannot be stored without
|
|
||||||
// loss of information.
|
|
||||||
RedisScan(src interface{}) error
|
|
||||||
}
|
|
479
vendor/github.com/garyburd/redigo/redis/reply.go
generated
vendored
479
vendor/github.com/garyburd/redigo/redis/reply.go
generated
vendored
|
@ -1,479 +0,0 @@
|
||||||
// Copyright 2012 Gary Burd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ErrNil indicates that a reply value is nil.
|
|
||||||
var ErrNil = errors.New("redigo: nil returned")
|
|
||||||
|
|
||||||
// Int is a helper that converts a command reply to an integer. If err is not
|
|
||||||
// equal to nil, then Int returns 0, err. Otherwise, Int converts the
|
|
||||||
// reply to an int as follows:
|
|
||||||
//
|
|
||||||
// Reply type Result
|
|
||||||
// integer int(reply), nil
|
|
||||||
// bulk string parsed reply, nil
|
|
||||||
// nil 0, ErrNil
|
|
||||||
// other 0, error
|
|
||||||
func Int(reply interface{}, err error) (int, error) {
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
switch reply := reply.(type) {
|
|
||||||
case int64:
|
|
||||||
x := int(reply)
|
|
||||||
if int64(x) != reply {
|
|
||||||
return 0, strconv.ErrRange
|
|
||||||
}
|
|
||||||
return x, nil
|
|
||||||
case []byte:
|
|
||||||
n, err := strconv.ParseInt(string(reply), 10, 0)
|
|
||||||
return int(n), err
|
|
||||||
case nil:
|
|
||||||
return 0, ErrNil
|
|
||||||
case Error:
|
|
||||||
return 0, reply
|
|
||||||
}
|
|
||||||
return 0, fmt.Errorf("redigo: unexpected type for Int, got type %T", reply)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int64 is a helper that converts a command reply to 64 bit integer. If err is
|
|
||||||
// not equal to nil, then Int returns 0, err. Otherwise, Int64 converts the
|
|
||||||
// reply to an int64 as follows:
|
|
||||||
//
|
|
||||||
// Reply type Result
|
|
||||||
// integer reply, nil
|
|
||||||
// bulk string parsed reply, nil
|
|
||||||
// nil 0, ErrNil
|
|
||||||
// other 0, error
|
|
||||||
func Int64(reply interface{}, err error) (int64, error) {
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
switch reply := reply.(type) {
|
|
||||||
case int64:
|
|
||||||
return reply, nil
|
|
||||||
case []byte:
|
|
||||||
n, err := strconv.ParseInt(string(reply), 10, 64)
|
|
||||||
return n, err
|
|
||||||
case nil:
|
|
||||||
return 0, ErrNil
|
|
||||||
case Error:
|
|
||||||
return 0, reply
|
|
||||||
}
|
|
||||||
return 0, fmt.Errorf("redigo: unexpected type for Int64, got type %T", reply)
|
|
||||||
}
|
|
||||||
|
|
||||||
var errNegativeInt = errors.New("redigo: unexpected value for Uint64")
|
|
||||||
|
|
||||||
// Uint64 is a helper that converts a command reply to 64 bit integer. If err is
|
|
||||||
// not equal to nil, then Int returns 0, err. Otherwise, Int64 converts the
|
|
||||||
// reply to an int64 as follows:
|
|
||||||
//
|
|
||||||
// Reply type Result
|
|
||||||
// integer reply, nil
|
|
||||||
// bulk string parsed reply, nil
|
|
||||||
// nil 0, ErrNil
|
|
||||||
// other 0, error
|
|
||||||
func Uint64(reply interface{}, err error) (uint64, error) {
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
switch reply := reply.(type) {
|
|
||||||
case int64:
|
|
||||||
if reply < 0 {
|
|
||||||
return 0, errNegativeInt
|
|
||||||
}
|
|
||||||
return uint64(reply), nil
|
|
||||||
case []byte:
|
|
||||||
n, err := strconv.ParseUint(string(reply), 10, 64)
|
|
||||||
return n, err
|
|
||||||
case nil:
|
|
||||||
return 0, ErrNil
|
|
||||||
case Error:
|
|
||||||
return 0, reply
|
|
||||||
}
|
|
||||||
return 0, fmt.Errorf("redigo: unexpected type for Uint64, got type %T", reply)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Float64 is a helper that converts a command reply to 64 bit float. If err is
|
|
||||||
// not equal to nil, then Float64 returns 0, err. Otherwise, Float64 converts
|
|
||||||
// the reply to an int as follows:
|
|
||||||
//
|
|
||||||
// Reply type Result
|
|
||||||
// bulk string parsed reply, nil
|
|
||||||
// nil 0, ErrNil
|
|
||||||
// other 0, error
|
|
||||||
func Float64(reply interface{}, err error) (float64, error) {
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
switch reply := reply.(type) {
|
|
||||||
case []byte:
|
|
||||||
n, err := strconv.ParseFloat(string(reply), 64)
|
|
||||||
return n, err
|
|
||||||
case nil:
|
|
||||||
return 0, ErrNil
|
|
||||||
case Error:
|
|
||||||
return 0, reply
|
|
||||||
}
|
|
||||||
return 0, fmt.Errorf("redigo: unexpected type for Float64, got type %T", reply)
|
|
||||||
}
|
|
||||||
|
|
||||||
// String is a helper that converts a command reply to a string. If err is not
|
|
||||||
// equal to nil, then String returns "", err. Otherwise String converts the
|
|
||||||
// reply to a string as follows:
|
|
||||||
//
|
|
||||||
// Reply type Result
|
|
||||||
// bulk string string(reply), nil
|
|
||||||
// simple string reply, nil
|
|
||||||
// nil "", ErrNil
|
|
||||||
// other "", error
|
|
||||||
func String(reply interface{}, err error) (string, error) {
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
switch reply := reply.(type) {
|
|
||||||
case []byte:
|
|
||||||
return string(reply), nil
|
|
||||||
case string:
|
|
||||||
return reply, nil
|
|
||||||
case nil:
|
|
||||||
return "", ErrNil
|
|
||||||
case Error:
|
|
||||||
return "", reply
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf("redigo: unexpected type for String, got type %T", reply)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes is a helper that converts a command reply to a slice of bytes. If err
|
|
||||||
// is not equal to nil, then Bytes returns nil, err. Otherwise Bytes converts
|
|
||||||
// the reply to a slice of bytes as follows:
|
|
||||||
//
|
|
||||||
// Reply type Result
|
|
||||||
// bulk string reply, nil
|
|
||||||
// simple string []byte(reply), nil
|
|
||||||
// nil nil, ErrNil
|
|
||||||
// other nil, error
|
|
||||||
func Bytes(reply interface{}, err error) ([]byte, error) {
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
switch reply := reply.(type) {
|
|
||||||
case []byte:
|
|
||||||
return reply, nil
|
|
||||||
case string:
|
|
||||||
return []byte(reply), nil
|
|
||||||
case nil:
|
|
||||||
return nil, ErrNil
|
|
||||||
case Error:
|
|
||||||
return nil, reply
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("redigo: unexpected type for Bytes, got type %T", reply)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bool is a helper that converts a command reply to a boolean. If err is not
|
|
||||||
// equal to nil, then Bool returns false, err. Otherwise Bool converts the
|
|
||||||
// reply to boolean as follows:
|
|
||||||
//
|
|
||||||
// Reply type Result
|
|
||||||
// integer value != 0, nil
|
|
||||||
// bulk string strconv.ParseBool(reply)
|
|
||||||
// nil false, ErrNil
|
|
||||||
// other false, error
|
|
||||||
func Bool(reply interface{}, err error) (bool, error) {
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
switch reply := reply.(type) {
|
|
||||||
case int64:
|
|
||||||
return reply != 0, nil
|
|
||||||
case []byte:
|
|
||||||
return strconv.ParseBool(string(reply))
|
|
||||||
case nil:
|
|
||||||
return false, ErrNil
|
|
||||||
case Error:
|
|
||||||
return false, reply
|
|
||||||
}
|
|
||||||
return false, fmt.Errorf("redigo: unexpected type for Bool, got type %T", reply)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MultiBulk is a helper that converts an array command reply to a []interface{}.
|
|
||||||
//
|
|
||||||
// Deprecated: Use Values instead.
|
|
||||||
func MultiBulk(reply interface{}, err error) ([]interface{}, error) { return Values(reply, err) }
|
|
||||||
|
|
||||||
// Values is a helper that converts an array command reply to a []interface{}.
|
|
||||||
// If err is not equal to nil, then Values returns nil, err. Otherwise, Values
|
|
||||||
// converts the reply as follows:
|
|
||||||
//
|
|
||||||
// Reply type Result
|
|
||||||
// array reply, nil
|
|
||||||
// nil nil, ErrNil
|
|
||||||
// other nil, error
|
|
||||||
func Values(reply interface{}, err error) ([]interface{}, error) {
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
switch reply := reply.(type) {
|
|
||||||
case []interface{}:
|
|
||||||
return reply, nil
|
|
||||||
case nil:
|
|
||||||
return nil, ErrNil
|
|
||||||
case Error:
|
|
||||||
return nil, reply
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("redigo: unexpected type for Values, got type %T", reply)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sliceHelper(reply interface{}, err error, name string, makeSlice func(int), assign func(int, interface{}) error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
switch reply := reply.(type) {
|
|
||||||
case []interface{}:
|
|
||||||
makeSlice(len(reply))
|
|
||||||
for i := range reply {
|
|
||||||
if reply[i] == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := assign(i, reply[i]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
case nil:
|
|
||||||
return ErrNil
|
|
||||||
case Error:
|
|
||||||
return reply
|
|
||||||
}
|
|
||||||
return fmt.Errorf("redigo: unexpected type for %s, got type %T", name, reply)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Float64s is a helper that converts an array command reply to a []float64. If
|
|
||||||
// err is not equal to nil, then Float64s returns nil, err. Nil array items are
|
|
||||||
// converted to 0 in the output slice. Floats64 returns an error if an array
|
|
||||||
// item is not a bulk string or nil.
|
|
||||||
func Float64s(reply interface{}, err error) ([]float64, error) {
|
|
||||||
var result []float64
|
|
||||||
err = sliceHelper(reply, err, "Float64s", func(n int) { result = make([]float64, n) }, func(i int, v interface{}) error {
|
|
||||||
p, ok := v.([]byte)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("redigo: unexpected element type for Floats64, got type %T", v)
|
|
||||||
}
|
|
||||||
f, err := strconv.ParseFloat(string(p), 64)
|
|
||||||
result[i] = f
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Strings is a helper that converts an array command reply to a []string. If
|
|
||||||
// err is not equal to nil, then Strings returns nil, err. Nil array items are
|
|
||||||
// converted to "" in the output slice. Strings returns an error if an array
|
|
||||||
// item is not a bulk string or nil.
|
|
||||||
func Strings(reply interface{}, err error) ([]string, error) {
|
|
||||||
var result []string
|
|
||||||
err = sliceHelper(reply, err, "Strings", func(n int) { result = make([]string, n) }, func(i int, v interface{}) error {
|
|
||||||
switch v := v.(type) {
|
|
||||||
case string:
|
|
||||||
result[i] = v
|
|
||||||
return nil
|
|
||||||
case []byte:
|
|
||||||
result[i] = string(v)
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("redigo: unexpected element type for Strings, got type %T", v)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ByteSlices is a helper that converts an array command reply to a [][]byte.
|
|
||||||
// If err is not equal to nil, then ByteSlices returns nil, err. Nil array
|
|
||||||
// items are stay nil. ByteSlices returns an error if an array item is not a
|
|
||||||
// bulk string or nil.
|
|
||||||
func ByteSlices(reply interface{}, err error) ([][]byte, error) {
|
|
||||||
var result [][]byte
|
|
||||||
err = sliceHelper(reply, err, "ByteSlices", func(n int) { result = make([][]byte, n) }, func(i int, v interface{}) error {
|
|
||||||
p, ok := v.([]byte)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("redigo: unexpected element type for ByteSlices, got type %T", v)
|
|
||||||
}
|
|
||||||
result[i] = p
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int64s is a helper that converts an array command reply to a []int64.
|
|
||||||
// If err is not equal to nil, then Int64s returns nil, err. Nil array
|
|
||||||
// items are stay nil. Int64s returns an error if an array item is not a
|
|
||||||
// bulk string or nil.
|
|
||||||
func Int64s(reply interface{}, err error) ([]int64, error) {
|
|
||||||
var result []int64
|
|
||||||
err = sliceHelper(reply, err, "Int64s", func(n int) { result = make([]int64, n) }, func(i int, v interface{}) error {
|
|
||||||
switch v := v.(type) {
|
|
||||||
case int64:
|
|
||||||
result[i] = v
|
|
||||||
return nil
|
|
||||||
case []byte:
|
|
||||||
n, err := strconv.ParseInt(string(v), 10, 64)
|
|
||||||
result[i] = n
|
|
||||||
return err
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("redigo: unexpected element type for Int64s, got type %T", v)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ints is a helper that converts an array command reply to a []in.
|
|
||||||
// If err is not equal to nil, then Ints returns nil, err. Nil array
|
|
||||||
// items are stay nil. Ints returns an error if an array item is not a
|
|
||||||
// bulk string or nil.
|
|
||||||
func Ints(reply interface{}, err error) ([]int, error) {
|
|
||||||
var result []int
|
|
||||||
err = sliceHelper(reply, err, "Ints", func(n int) { result = make([]int, n) }, func(i int, v interface{}) error {
|
|
||||||
switch v := v.(type) {
|
|
||||||
case int64:
|
|
||||||
n := int(v)
|
|
||||||
if int64(n) != v {
|
|
||||||
return strconv.ErrRange
|
|
||||||
}
|
|
||||||
result[i] = n
|
|
||||||
return nil
|
|
||||||
case []byte:
|
|
||||||
n, err := strconv.Atoi(string(v))
|
|
||||||
result[i] = n
|
|
||||||
return err
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("redigo: unexpected element type for Ints, got type %T", v)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringMap is a helper that converts an array of strings (alternating key, value)
|
|
||||||
// into a map[string]string. The HGETALL and CONFIG GET commands return replies in this format.
|
|
||||||
// Requires an even number of values in result.
|
|
||||||
func StringMap(result interface{}, err error) (map[string]string, error) {
|
|
||||||
values, err := Values(result, err)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(values)%2 != 0 {
|
|
||||||
return nil, errors.New("redigo: StringMap expects even number of values result")
|
|
||||||
}
|
|
||||||
m := make(map[string]string, len(values)/2)
|
|
||||||
for i := 0; i < len(values); i += 2 {
|
|
||||||
key, okKey := values[i].([]byte)
|
|
||||||
value, okValue := values[i+1].([]byte)
|
|
||||||
if !okKey || !okValue {
|
|
||||||
return nil, errors.New("redigo: StringMap key not a bulk string value")
|
|
||||||
}
|
|
||||||
m[string(key)] = string(value)
|
|
||||||
}
|
|
||||||
return m, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IntMap is a helper that converts an array of strings (alternating key, value)
|
|
||||||
// into a map[string]int. The HGETALL commands return replies in this format.
|
|
||||||
// Requires an even number of values in result.
|
|
||||||
func IntMap(result interface{}, err error) (map[string]int, error) {
|
|
||||||
values, err := Values(result, err)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(values)%2 != 0 {
|
|
||||||
return nil, errors.New("redigo: IntMap expects even number of values result")
|
|
||||||
}
|
|
||||||
m := make(map[string]int, len(values)/2)
|
|
||||||
for i := 0; i < len(values); i += 2 {
|
|
||||||
key, ok := values[i].([]byte)
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("redigo: IntMap key not a bulk string value")
|
|
||||||
}
|
|
||||||
value, err := Int(values[i+1], nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
m[string(key)] = value
|
|
||||||
}
|
|
||||||
return m, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int64Map is a helper that converts an array of strings (alternating key, value)
|
|
||||||
// into a map[string]int64. The HGETALL commands return replies in this format.
|
|
||||||
// Requires an even number of values in result.
|
|
||||||
func Int64Map(result interface{}, err error) (map[string]int64, error) {
|
|
||||||
values, err := Values(result, err)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(values)%2 != 0 {
|
|
||||||
return nil, errors.New("redigo: Int64Map expects even number of values result")
|
|
||||||
}
|
|
||||||
m := make(map[string]int64, len(values)/2)
|
|
||||||
for i := 0; i < len(values); i += 2 {
|
|
||||||
key, ok := values[i].([]byte)
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("redigo: Int64Map key not a bulk string value")
|
|
||||||
}
|
|
||||||
value, err := Int64(values[i+1], nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
m[string(key)] = value
|
|
||||||
}
|
|
||||||
return m, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Positions is a helper that converts an array of positions (lat, long)
|
|
||||||
// into a [][2]float64. The GEOPOS command returns replies in this format.
|
|
||||||
func Positions(result interface{}, err error) ([]*[2]float64, error) {
|
|
||||||
values, err := Values(result, err)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
positions := make([]*[2]float64, len(values))
|
|
||||||
for i := range values {
|
|
||||||
if values[i] == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
p, ok := values[i].([]interface{})
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("redigo: unexpected element type for interface slice, got type %T", values[i])
|
|
||||||
}
|
|
||||||
if len(p) != 2 {
|
|
||||||
return nil, fmt.Errorf("redigo: unexpected number of values for a member position, got %d", len(p))
|
|
||||||
}
|
|
||||||
lat, err := Float64(p[0], nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
long, err := Float64(p[1], nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
positions[i] = &[2]float64{lat, long}
|
|
||||||
}
|
|
||||||
return positions, nil
|
|
||||||
}
|
|
585
vendor/github.com/garyburd/redigo/redis/scan.go
generated
vendored
585
vendor/github.com/garyburd/redigo/redis/scan.go
generated
vendored
|
@ -1,585 +0,0 @@
|
||||||
// Copyright 2012 Gary Burd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ensureLen(d reflect.Value, n int) {
|
|
||||||
if n > d.Cap() {
|
|
||||||
d.Set(reflect.MakeSlice(d.Type(), n, n))
|
|
||||||
} else {
|
|
||||||
d.SetLen(n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func cannotConvert(d reflect.Value, s interface{}) error {
|
|
||||||
var sname string
|
|
||||||
switch s.(type) {
|
|
||||||
case string:
|
|
||||||
sname = "Redis simple string"
|
|
||||||
case Error:
|
|
||||||
sname = "Redis error"
|
|
||||||
case int64:
|
|
||||||
sname = "Redis integer"
|
|
||||||
case []byte:
|
|
||||||
sname = "Redis bulk string"
|
|
||||||
case []interface{}:
|
|
||||||
sname = "Redis array"
|
|
||||||
default:
|
|
||||||
sname = reflect.TypeOf(s).String()
|
|
||||||
}
|
|
||||||
return fmt.Errorf("cannot convert from %s to %s", sname, d.Type())
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertAssignBulkString(d reflect.Value, s []byte) (err error) {
|
|
||||||
switch d.Type().Kind() {
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
var x float64
|
|
||||||
x, err = strconv.ParseFloat(string(s), d.Type().Bits())
|
|
||||||
d.SetFloat(x)
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
var x int64
|
|
||||||
x, err = strconv.ParseInt(string(s), 10, d.Type().Bits())
|
|
||||||
d.SetInt(x)
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
||||||
var x uint64
|
|
||||||
x, err = strconv.ParseUint(string(s), 10, d.Type().Bits())
|
|
||||||
d.SetUint(x)
|
|
||||||
case reflect.Bool:
|
|
||||||
var x bool
|
|
||||||
x, err = strconv.ParseBool(string(s))
|
|
||||||
d.SetBool(x)
|
|
||||||
case reflect.String:
|
|
||||||
d.SetString(string(s))
|
|
||||||
case reflect.Slice:
|
|
||||||
if d.Type().Elem().Kind() != reflect.Uint8 {
|
|
||||||
err = cannotConvert(d, s)
|
|
||||||
} else {
|
|
||||||
d.SetBytes(s)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
err = cannotConvert(d, s)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertAssignInt(d reflect.Value, s int64) (err error) {
|
|
||||||
switch d.Type().Kind() {
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
d.SetInt(s)
|
|
||||||
if d.Int() != s {
|
|
||||||
err = strconv.ErrRange
|
|
||||||
d.SetInt(0)
|
|
||||||
}
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
||||||
if s < 0 {
|
|
||||||
err = strconv.ErrRange
|
|
||||||
} else {
|
|
||||||
x := uint64(s)
|
|
||||||
d.SetUint(x)
|
|
||||||
if d.Uint() != x {
|
|
||||||
err = strconv.ErrRange
|
|
||||||
d.SetUint(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Bool:
|
|
||||||
d.SetBool(s != 0)
|
|
||||||
default:
|
|
||||||
err = cannotConvert(d, s)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertAssignValue(d reflect.Value, s interface{}) (err error) {
|
|
||||||
if d.Kind() != reflect.Ptr {
|
|
||||||
if d.CanAddr() {
|
|
||||||
d2 := d.Addr()
|
|
||||||
if d2.CanInterface() {
|
|
||||||
if scanner, ok := d2.Interface().(Scanner); ok {
|
|
||||||
return scanner.RedisScan(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if d.CanInterface() {
|
|
||||||
// Already a reflect.Ptr
|
|
||||||
if d.IsNil() {
|
|
||||||
d.Set(reflect.New(d.Type().Elem()))
|
|
||||||
}
|
|
||||||
if scanner, ok := d.Interface().(Scanner); ok {
|
|
||||||
return scanner.RedisScan(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch s := s.(type) {
|
|
||||||
case []byte:
|
|
||||||
err = convertAssignBulkString(d, s)
|
|
||||||
case int64:
|
|
||||||
err = convertAssignInt(d, s)
|
|
||||||
default:
|
|
||||||
err = cannotConvert(d, s)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertAssignArray(d reflect.Value, s []interface{}) error {
|
|
||||||
if d.Type().Kind() != reflect.Slice {
|
|
||||||
return cannotConvert(d, s)
|
|
||||||
}
|
|
||||||
ensureLen(d, len(s))
|
|
||||||
for i := 0; i < len(s); i++ {
|
|
||||||
if err := convertAssignValue(d.Index(i), s[i]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertAssign(d interface{}, s interface{}) (err error) {
|
|
||||||
if scanner, ok := d.(Scanner); ok {
|
|
||||||
return scanner.RedisScan(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle the most common destination types using type switches and
|
|
||||||
// fall back to reflection for all other types.
|
|
||||||
switch s := s.(type) {
|
|
||||||
case nil:
|
|
||||||
// ignore
|
|
||||||
case []byte:
|
|
||||||
switch d := d.(type) {
|
|
||||||
case *string:
|
|
||||||
*d = string(s)
|
|
||||||
case *int:
|
|
||||||
*d, err = strconv.Atoi(string(s))
|
|
||||||
case *bool:
|
|
||||||
*d, err = strconv.ParseBool(string(s))
|
|
||||||
case *[]byte:
|
|
||||||
*d = s
|
|
||||||
case *interface{}:
|
|
||||||
*d = s
|
|
||||||
case nil:
|
|
||||||
// skip value
|
|
||||||
default:
|
|
||||||
if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
|
|
||||||
err = cannotConvert(d, s)
|
|
||||||
} else {
|
|
||||||
err = convertAssignBulkString(d.Elem(), s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case int64:
|
|
||||||
switch d := d.(type) {
|
|
||||||
case *int:
|
|
||||||
x := int(s)
|
|
||||||
if int64(x) != s {
|
|
||||||
err = strconv.ErrRange
|
|
||||||
x = 0
|
|
||||||
}
|
|
||||||
*d = x
|
|
||||||
case *bool:
|
|
||||||
*d = s != 0
|
|
||||||
case *interface{}:
|
|
||||||
*d = s
|
|
||||||
case nil:
|
|
||||||
// skip value
|
|
||||||
default:
|
|
||||||
if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
|
|
||||||
err = cannotConvert(d, s)
|
|
||||||
} else {
|
|
||||||
err = convertAssignInt(d.Elem(), s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case string:
|
|
||||||
switch d := d.(type) {
|
|
||||||
case *string:
|
|
||||||
*d = s
|
|
||||||
case *interface{}:
|
|
||||||
*d = s
|
|
||||||
case nil:
|
|
||||||
// skip value
|
|
||||||
default:
|
|
||||||
err = cannotConvert(reflect.ValueOf(d), s)
|
|
||||||
}
|
|
||||||
case []interface{}:
|
|
||||||
switch d := d.(type) {
|
|
||||||
case *[]interface{}:
|
|
||||||
*d = s
|
|
||||||
case *interface{}:
|
|
||||||
*d = s
|
|
||||||
case nil:
|
|
||||||
// skip value
|
|
||||||
default:
|
|
||||||
if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
|
|
||||||
err = cannotConvert(d, s)
|
|
||||||
} else {
|
|
||||||
err = convertAssignArray(d.Elem(), s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case Error:
|
|
||||||
err = s
|
|
||||||
default:
|
|
||||||
err = cannotConvert(reflect.ValueOf(d), s)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan copies from src to the values pointed at by dest.
|
|
||||||
//
|
|
||||||
// Scan uses RedisScan if available otherwise:
|
|
||||||
//
|
|
||||||
// The values pointed at by dest must be an integer, float, boolean, string,
|
|
||||||
// []byte, interface{} or slices of these types. Scan uses the standard strconv
|
|
||||||
// package to convert bulk strings to numeric and boolean types.
|
|
||||||
//
|
|
||||||
// If a dest value is nil, then the corresponding src value is skipped.
|
|
||||||
//
|
|
||||||
// If a src element is nil, then the corresponding dest value is not modified.
|
|
||||||
//
|
|
||||||
// To enable easy use of Scan in a loop, Scan returns the slice of src
|
|
||||||
// following the copied values.
|
|
||||||
func Scan(src []interface{}, dest ...interface{}) ([]interface{}, error) {
|
|
||||||
if len(src) < len(dest) {
|
|
||||||
return nil, errors.New("redigo.Scan: array short")
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
for i, d := range dest {
|
|
||||||
err = convertAssign(d, src[i])
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("redigo.Scan: cannot assign to dest %d: %v", i, err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return src[len(dest):], err
|
|
||||||
}
|
|
||||||
|
|
||||||
type fieldSpec struct {
|
|
||||||
name string
|
|
||||||
index []int
|
|
||||||
omitEmpty bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type structSpec struct {
|
|
||||||
m map[string]*fieldSpec
|
|
||||||
l []*fieldSpec
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ss *structSpec) fieldSpec(name []byte) *fieldSpec {
|
|
||||||
return ss.m[string(name)]
|
|
||||||
}
|
|
||||||
|
|
||||||
func compileStructSpec(t reflect.Type, depth map[string]int, index []int, ss *structSpec) {
|
|
||||||
for i := 0; i < t.NumField(); i++ {
|
|
||||||
f := t.Field(i)
|
|
||||||
switch {
|
|
||||||
case f.PkgPath != "" && !f.Anonymous:
|
|
||||||
// Ignore unexported fields.
|
|
||||||
case f.Anonymous:
|
|
||||||
// TODO: Handle pointers. Requires change to decoder and
|
|
||||||
// protection against infinite recursion.
|
|
||||||
if f.Type.Kind() == reflect.Struct {
|
|
||||||
compileStructSpec(f.Type, depth, append(index, i), ss)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
fs := &fieldSpec{name: f.Name}
|
|
||||||
tag := f.Tag.Get("redis")
|
|
||||||
p := strings.Split(tag, ",")
|
|
||||||
if len(p) > 0 {
|
|
||||||
if p[0] == "-" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if len(p[0]) > 0 {
|
|
||||||
fs.name = p[0]
|
|
||||||
}
|
|
||||||
for _, s := range p[1:] {
|
|
||||||
switch s {
|
|
||||||
case "omitempty":
|
|
||||||
fs.omitEmpty = true
|
|
||||||
default:
|
|
||||||
panic(fmt.Errorf("redigo: unknown field tag %s for type %s", s, t.Name()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
d, found := depth[fs.name]
|
|
||||||
if !found {
|
|
||||||
d = 1 << 30
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case len(index) == d:
|
|
||||||
// At same depth, remove from result.
|
|
||||||
delete(ss.m, fs.name)
|
|
||||||
j := 0
|
|
||||||
for i := 0; i < len(ss.l); i++ {
|
|
||||||
if fs.name != ss.l[i].name {
|
|
||||||
ss.l[j] = ss.l[i]
|
|
||||||
j += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ss.l = ss.l[:j]
|
|
||||||
case len(index) < d:
|
|
||||||
fs.index = make([]int, len(index)+1)
|
|
||||||
copy(fs.index, index)
|
|
||||||
fs.index[len(index)] = i
|
|
||||||
depth[fs.name] = len(index)
|
|
||||||
ss.m[fs.name] = fs
|
|
||||||
ss.l = append(ss.l, fs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
structSpecMutex sync.RWMutex
|
|
||||||
structSpecCache = make(map[reflect.Type]*structSpec)
|
|
||||||
defaultFieldSpec = &fieldSpec{}
|
|
||||||
)
|
|
||||||
|
|
||||||
func structSpecForType(t reflect.Type) *structSpec {
|
|
||||||
|
|
||||||
structSpecMutex.RLock()
|
|
||||||
ss, found := structSpecCache[t]
|
|
||||||
structSpecMutex.RUnlock()
|
|
||||||
if found {
|
|
||||||
return ss
|
|
||||||
}
|
|
||||||
|
|
||||||
structSpecMutex.Lock()
|
|
||||||
defer structSpecMutex.Unlock()
|
|
||||||
ss, found = structSpecCache[t]
|
|
||||||
if found {
|
|
||||||
return ss
|
|
||||||
}
|
|
||||||
|
|
||||||
ss = &structSpec{m: make(map[string]*fieldSpec)}
|
|
||||||
compileStructSpec(t, make(map[string]int), nil, ss)
|
|
||||||
structSpecCache[t] = ss
|
|
||||||
return ss
|
|
||||||
}
|
|
||||||
|
|
||||||
var errScanStructValue = errors.New("redigo.ScanStruct: value must be non-nil pointer to a struct")
|
|
||||||
|
|
||||||
// ScanStruct scans alternating names and values from src to a struct. The
|
|
||||||
// HGETALL and CONFIG GET commands return replies in this format.
|
|
||||||
//
|
|
||||||
// ScanStruct uses exported field names to match values in the response. Use
|
|
||||||
// 'redis' field tag to override the name:
|
|
||||||
//
|
|
||||||
// Field int `redis:"myName"`
|
|
||||||
//
|
|
||||||
// Fields with the tag redis:"-" are ignored.
|
|
||||||
//
|
|
||||||
// Each field uses RedisScan if available otherwise:
|
|
||||||
// Integer, float, boolean, string and []byte fields are supported. Scan uses the
|
|
||||||
// standard strconv package to convert bulk string values to numeric and
|
|
||||||
// boolean types.
|
|
||||||
//
|
|
||||||
// If a src element is nil, then the corresponding field is not modified.
|
|
||||||
func ScanStruct(src []interface{}, dest interface{}) error {
|
|
||||||
d := reflect.ValueOf(dest)
|
|
||||||
if d.Kind() != reflect.Ptr || d.IsNil() {
|
|
||||||
return errScanStructValue
|
|
||||||
}
|
|
||||||
d = d.Elem()
|
|
||||||
if d.Kind() != reflect.Struct {
|
|
||||||
return errScanStructValue
|
|
||||||
}
|
|
||||||
ss := structSpecForType(d.Type())
|
|
||||||
|
|
||||||
if len(src)%2 != 0 {
|
|
||||||
return errors.New("redigo.ScanStruct: number of values not a multiple of 2")
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(src); i += 2 {
|
|
||||||
s := src[i+1]
|
|
||||||
if s == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
name, ok := src[i].([]byte)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("redigo.ScanStruct: key %d not a bulk string value", i)
|
|
||||||
}
|
|
||||||
fs := ss.fieldSpec(name)
|
|
||||||
if fs == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := convertAssignValue(d.FieldByIndex(fs.index), s); err != nil {
|
|
||||||
return fmt.Errorf("redigo.ScanStruct: cannot assign field %s: %v", fs.name, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
errScanSliceValue = errors.New("redigo.ScanSlice: dest must be non-nil pointer to a struct")
|
|
||||||
)
|
|
||||||
|
|
||||||
// ScanSlice scans src to the slice pointed to by dest. The elements the dest
|
|
||||||
// slice must be integer, float, boolean, string, struct or pointer to struct
|
|
||||||
// values.
|
|
||||||
//
|
|
||||||
// Struct fields must be integer, float, boolean or string values. All struct
|
|
||||||
// fields are used unless a subset is specified using fieldNames.
|
|
||||||
func ScanSlice(src []interface{}, dest interface{}, fieldNames ...string) error {
|
|
||||||
d := reflect.ValueOf(dest)
|
|
||||||
if d.Kind() != reflect.Ptr || d.IsNil() {
|
|
||||||
return errScanSliceValue
|
|
||||||
}
|
|
||||||
d = d.Elem()
|
|
||||||
if d.Kind() != reflect.Slice {
|
|
||||||
return errScanSliceValue
|
|
||||||
}
|
|
||||||
|
|
||||||
isPtr := false
|
|
||||||
t := d.Type().Elem()
|
|
||||||
if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
|
|
||||||
isPtr = true
|
|
||||||
t = t.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.Kind() != reflect.Struct {
|
|
||||||
ensureLen(d, len(src))
|
|
||||||
for i, s := range src {
|
|
||||||
if s == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := convertAssignValue(d.Index(i), s); err != nil {
|
|
||||||
return fmt.Errorf("redigo.ScanSlice: cannot assign element %d: %v", i, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ss := structSpecForType(t)
|
|
||||||
fss := ss.l
|
|
||||||
if len(fieldNames) > 0 {
|
|
||||||
fss = make([]*fieldSpec, len(fieldNames))
|
|
||||||
for i, name := range fieldNames {
|
|
||||||
fss[i] = ss.m[name]
|
|
||||||
if fss[i] == nil {
|
|
||||||
return fmt.Errorf("redigo.ScanSlice: ScanSlice bad field name %s", name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(fss) == 0 {
|
|
||||||
return errors.New("redigo.ScanSlice: no struct fields")
|
|
||||||
}
|
|
||||||
|
|
||||||
n := len(src) / len(fss)
|
|
||||||
if n*len(fss) != len(src) {
|
|
||||||
return errors.New("redigo.ScanSlice: length not a multiple of struct field count")
|
|
||||||
}
|
|
||||||
|
|
||||||
ensureLen(d, n)
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
d := d.Index(i)
|
|
||||||
if isPtr {
|
|
||||||
if d.IsNil() {
|
|
||||||
d.Set(reflect.New(t))
|
|
||||||
}
|
|
||||||
d = d.Elem()
|
|
||||||
}
|
|
||||||
for j, fs := range fss {
|
|
||||||
s := src[i*len(fss)+j]
|
|
||||||
if s == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := convertAssignValue(d.FieldByIndex(fs.index), s); err != nil {
|
|
||||||
return fmt.Errorf("redigo.ScanSlice: cannot assign element %d to field %s: %v", i*len(fss)+j, fs.name, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Args is a helper for constructing command arguments from structured values.
|
|
||||||
type Args []interface{}
|
|
||||||
|
|
||||||
// Add returns the result of appending value to args.
|
|
||||||
func (args Args) Add(value ...interface{}) Args {
|
|
||||||
return append(args, value...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddFlat returns the result of appending the flattened value of v to args.
|
|
||||||
//
|
|
||||||
// Maps are flattened by appending the alternating keys and map values to args.
|
|
||||||
//
|
|
||||||
// Slices are flattened by appending the slice elements to args.
|
|
||||||
//
|
|
||||||
// Structs are flattened by appending the alternating names and values of
|
|
||||||
// exported fields to args. If v is a nil struct pointer, then nothing is
|
|
||||||
// appended. The 'redis' field tag overrides struct field names. See ScanStruct
|
|
||||||
// for more information on the use of the 'redis' field tag.
|
|
||||||
//
|
|
||||||
// Other types are appended to args as is.
|
|
||||||
func (args Args) AddFlat(v interface{}) Args {
|
|
||||||
rv := reflect.ValueOf(v)
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Struct:
|
|
||||||
args = flattenStruct(args, rv)
|
|
||||||
case reflect.Slice:
|
|
||||||
for i := 0; i < rv.Len(); i++ {
|
|
||||||
args = append(args, rv.Index(i).Interface())
|
|
||||||
}
|
|
||||||
case reflect.Map:
|
|
||||||
for _, k := range rv.MapKeys() {
|
|
||||||
args = append(args, k.Interface(), rv.MapIndex(k).Interface())
|
|
||||||
}
|
|
||||||
case reflect.Ptr:
|
|
||||||
if rv.Type().Elem().Kind() == reflect.Struct {
|
|
||||||
if !rv.IsNil() {
|
|
||||||
args = flattenStruct(args, rv.Elem())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
args = append(args, v)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
args = append(args, v)
|
|
||||||
}
|
|
||||||
return args
|
|
||||||
}
|
|
||||||
|
|
||||||
func flattenStruct(args Args, v reflect.Value) Args {
|
|
||||||
ss := structSpecForType(v.Type())
|
|
||||||
for _, fs := range ss.l {
|
|
||||||
fv := v.FieldByIndex(fs.index)
|
|
||||||
if fs.omitEmpty {
|
|
||||||
var empty = false
|
|
||||||
switch fv.Kind() {
|
|
||||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
|
||||||
empty = fv.Len() == 0
|
|
||||||
case reflect.Bool:
|
|
||||||
empty = !fv.Bool()
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
empty = fv.Int() == 0
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
||||||
empty = fv.Uint() == 0
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
empty = fv.Float() == 0
|
|
||||||
case reflect.Interface, reflect.Ptr:
|
|
||||||
empty = fv.IsNil()
|
|
||||||
}
|
|
||||||
if empty {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
args = append(args, fs.name, fv.Interface())
|
|
||||||
}
|
|
||||||
return args
|
|
||||||
}
|
|
91
vendor/github.com/garyburd/redigo/redis/script.go
generated
vendored
91
vendor/github.com/garyburd/redigo/redis/script.go
generated
vendored
|
@ -1,91 +0,0 @@
|
||||||
// Copyright 2012 Gary Burd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha1"
|
|
||||||
"encoding/hex"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Script encapsulates the source, hash and key count for a Lua script. See
|
|
||||||
// http://redis.io/commands/eval for information on scripts in Redis.
|
|
||||||
type Script struct {
|
|
||||||
keyCount int
|
|
||||||
src string
|
|
||||||
hash string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewScript returns a new script object. If keyCount is greater than or equal
|
|
||||||
// to zero, then the count is automatically inserted in the EVAL command
|
|
||||||
// argument list. If keyCount is less than zero, then the application supplies
|
|
||||||
// the count as the first value in the keysAndArgs argument to the Do, Send and
|
|
||||||
// SendHash methods.
|
|
||||||
func NewScript(keyCount int, src string) *Script {
|
|
||||||
h := sha1.New()
|
|
||||||
io.WriteString(h, src)
|
|
||||||
return &Script{keyCount, src, hex.EncodeToString(h.Sum(nil))}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Script) args(spec string, keysAndArgs []interface{}) []interface{} {
|
|
||||||
var args []interface{}
|
|
||||||
if s.keyCount < 0 {
|
|
||||||
args = make([]interface{}, 1+len(keysAndArgs))
|
|
||||||
args[0] = spec
|
|
||||||
copy(args[1:], keysAndArgs)
|
|
||||||
} else {
|
|
||||||
args = make([]interface{}, 2+len(keysAndArgs))
|
|
||||||
args[0] = spec
|
|
||||||
args[1] = s.keyCount
|
|
||||||
copy(args[2:], keysAndArgs)
|
|
||||||
}
|
|
||||||
return args
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash returns the script hash.
|
|
||||||
func (s *Script) Hash() string {
|
|
||||||
return s.hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do evaluates the script. Under the covers, Do optimistically evaluates the
|
|
||||||
// script using the EVALSHA command. If the command fails because the script is
|
|
||||||
// not loaded, then Do evaluates the script using the EVAL command (thus
|
|
||||||
// causing the script to load).
|
|
||||||
func (s *Script) Do(c Conn, keysAndArgs ...interface{}) (interface{}, error) {
|
|
||||||
v, err := c.Do("EVALSHA", s.args(s.hash, keysAndArgs)...)
|
|
||||||
if e, ok := err.(Error); ok && strings.HasPrefix(string(e), "NOSCRIPT ") {
|
|
||||||
v, err = c.Do("EVAL", s.args(s.src, keysAndArgs)...)
|
|
||||||
}
|
|
||||||
return v, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendHash evaluates the script without waiting for the reply. The script is
|
|
||||||
// evaluated with the EVALSHA command. The application must ensure that the
|
|
||||||
// script is loaded by a previous call to Send, Do or Load methods.
|
|
||||||
func (s *Script) SendHash(c Conn, keysAndArgs ...interface{}) error {
|
|
||||||
return c.Send("EVALSHA", s.args(s.hash, keysAndArgs)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send evaluates the script without waiting for the reply.
|
|
||||||
func (s *Script) Send(c Conn, keysAndArgs ...interface{}) error {
|
|
||||||
return c.Send("EVAL", s.args(s.src, keysAndArgs)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load loads the script without evaluating it.
|
|
||||||
func (s *Script) Load(c Conn) error {
|
|
||||||
_, err := c.Do("SCRIPT", "LOAD", s.src)
|
|
||||||
return err
|
|
||||||
}
|
|
5
vendor/github.com/go-ini/ini/.gitignore
generated
vendored
Normal file
5
vendor/github.com/go-ini/ini/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
testdata/conf_out.ini
|
||||||
|
ini.sublime-project
|
||||||
|
ini.sublime-workspace
|
||||||
|
testdata/conf_reflect.ini
|
||||||
|
.idea
|
14
vendor/github.com/go-ini/ini/.travis.yml
generated
vendored
Normal file
14
vendor/github.com/go-ini/ini/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
sudo: false
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.4.x
|
||||||
|
- 1.5.x
|
||||||
|
- 1.6.x
|
||||||
|
- 1.7.x
|
||||||
|
- 1.8.x
|
||||||
|
- master
|
||||||
|
|
||||||
|
script:
|
||||||
|
- go get golang.org/x/tools/cmd/cover
|
||||||
|
- go get github.com/smartystreets/goconvey
|
||||||
|
- go test -v -cover -race
|
9
vendor/github.com/go-sql-driver/mysql/.gitignore
generated
vendored
Normal file
9
vendor/github.com/go-sql-driver/mysql/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
.DS_Store
|
||||||
|
.DS_Store?
|
||||||
|
._*
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
Icon?
|
||||||
|
ehthumbs.db
|
||||||
|
Thumbs.db
|
||||||
|
.idea
|
93
vendor/github.com/go-sql-driver/mysql/.travis.yml
generated
vendored
Normal file
93
vendor/github.com/go-sql-driver/mysql/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
sudo: false
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.5
|
||||||
|
- 1.6
|
||||||
|
- 1.7
|
||||||
|
- 1.8
|
||||||
|
- 1.9
|
||||||
|
- tip
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- go get golang.org/x/tools/cmd/cover
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- echo -e "[server]\ninnodb_log_file_size=256MB\ninnodb_buffer_pool_size=512MB\nmax_allowed_packet=16MB" | sudo tee -a /etc/mysql/my.cnf
|
||||||
|
- sudo service mysql restart
|
||||||
|
- .travis/wait_mysql.sh
|
||||||
|
- mysql -e 'create database gotest;'
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- env: DB=MYSQL57
|
||||||
|
sudo: required
|
||||||
|
dist: trusty
|
||||||
|
go: 1.9
|
||||||
|
services:
|
||||||
|
- docker
|
||||||
|
before_install:
|
||||||
|
- go get golang.org/x/tools/cmd/cover
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
- docker pull mysql:5.7
|
||||||
|
- docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret
|
||||||
|
mysql:5.7 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB
|
||||||
|
- sleep 30
|
||||||
|
- cp .travis/docker.cnf ~/.my.cnf
|
||||||
|
- mysql --print-defaults
|
||||||
|
- .travis/wait_mysql.sh
|
||||||
|
before_script:
|
||||||
|
- export MYSQL_TEST_USER=gotest
|
||||||
|
- export MYSQL_TEST_PASS=secret
|
||||||
|
- export MYSQL_TEST_ADDR=127.0.0.1:3307
|
||||||
|
- export MYSQL_TEST_CONCURRENT=1
|
||||||
|
|
||||||
|
- env: DB=MARIA55
|
||||||
|
sudo: required
|
||||||
|
dist: trusty
|
||||||
|
go: 1.9
|
||||||
|
services:
|
||||||
|
- docker
|
||||||
|
before_install:
|
||||||
|
- go get golang.org/x/tools/cmd/cover
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
- docker pull mariadb:5.5
|
||||||
|
- docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret
|
||||||
|
mariadb:5.5 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB
|
||||||
|
- sleep 30
|
||||||
|
- cp .travis/docker.cnf ~/.my.cnf
|
||||||
|
- mysql --print-defaults
|
||||||
|
- .travis/wait_mysql.sh
|
||||||
|
before_script:
|
||||||
|
- export MYSQL_TEST_USER=gotest
|
||||||
|
- export MYSQL_TEST_PASS=secret
|
||||||
|
- export MYSQL_TEST_ADDR=127.0.0.1:3307
|
||||||
|
- export MYSQL_TEST_CONCURRENT=1
|
||||||
|
|
||||||
|
- env: DB=MARIA10_1
|
||||||
|
sudo: required
|
||||||
|
dist: trusty
|
||||||
|
go: 1.9
|
||||||
|
services:
|
||||||
|
- docker
|
||||||
|
before_install:
|
||||||
|
- go get golang.org/x/tools/cmd/cover
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
- docker pull mariadb:10.1
|
||||||
|
- docker run -d -p 127.0.0.1:3307:3306 --name mysqld -e MYSQL_DATABASE=gotest -e MYSQL_USER=gotest -e MYSQL_PASSWORD=secret -e MYSQL_ROOT_PASSWORD=verysecret
|
||||||
|
mariadb:10.1 --innodb_log_file_size=256MB --innodb_buffer_pool_size=512MB --max_allowed_packet=16MB
|
||||||
|
- sleep 30
|
||||||
|
- cp .travis/docker.cnf ~/.my.cnf
|
||||||
|
- mysql --print-defaults
|
||||||
|
- .travis/wait_mysql.sh
|
||||||
|
before_script:
|
||||||
|
- export MYSQL_TEST_USER=gotest
|
||||||
|
- export MYSQL_TEST_PASS=secret
|
||||||
|
- export MYSQL_TEST_ADDR=127.0.0.1:3307
|
||||||
|
- export MYSQL_TEST_CONCURRENT=1
|
||||||
|
|
||||||
|
script:
|
||||||
|
- go test -v -covermode=count -coverprofile=coverage.out
|
||||||
|
- go vet ./...
|
||||||
|
- test -z "$(gofmt -d -s . | tee /dev/stderr)"
|
||||||
|
- $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci
|
1
vendor/github.com/go-xorm/core/.gitignore
generated
vendored
Normal file
1
vendor/github.com/go-xorm/core/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
*.db
|
0
vendor/github.com/go-xorm/core/benchmark.sh
generated
vendored
Executable file → Normal file
0
vendor/github.com/go-xorm/core/benchmark.sh
generated
vendored
Executable file → Normal file
28
vendor/github.com/go-xorm/xorm-redis-cache/LICENSE
generated
vendored
28
vendor/github.com/go-xorm/xorm-redis-cache/LICENSE
generated
vendored
|
@ -1,28 +0,0 @@
|
||||||
Copyright (c) 2014, go-xorm
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of the {organization} nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
9
vendor/github.com/go-xorm/xorm-redis-cache/README.md
generated
vendored
9
vendor/github.com/go-xorm/xorm-redis-cache/README.md
generated
vendored
|
@ -1,9 +0,0 @@
|
||||||
xorm-redis-cache
|
|
||||||
================
|
|
||||||
|
|
||||||
XORM Redis Cache
|
|
||||||
|
|
||||||
|
|
||||||
[![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/go-xorm/xorm-redis-cache)
|
|
||||||
|
|
||||||
|
|
302
vendor/github.com/go-xorm/xorm-redis-cache/redis_cacher.go
generated
vendored
302
vendor/github.com/go-xorm/xorm-redis-cache/redis_cacher.go
generated
vendored
|
@ -1,302 +0,0 @@
|
||||||
package xormrediscache
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/gob"
|
|
||||||
"fmt"
|
|
||||||
"github.com/garyburd/redigo/redis"
|
|
||||||
"github.com/go-xorm/core"
|
|
||||||
"hash/crc32"
|
|
||||||
// "log"
|
|
||||||
"reflect"
|
|
||||||
// "strconv"
|
|
||||||
"time"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
DEFAULT_EXPIRATION = time.Duration(0)
|
|
||||||
FOREVER_EXPIRATION = time.Duration(-1)
|
|
||||||
|
|
||||||
LOGGING_PREFIX = "[redis_cacher]"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Wraps the Redis client to meet the Cache interface.
|
|
||||||
type RedisCacher struct {
|
|
||||||
pool *redis.Pool
|
|
||||||
defaultExpiration time.Duration
|
|
||||||
|
|
||||||
Logger core.ILogger
|
|
||||||
}
|
|
||||||
|
|
||||||
// New a Redis Cacher, host as IP endpoint, i.e., localhost:6379, provide empty string or nil if Redis server doesn't
|
|
||||||
// require AUTH command, defaultExpiration sets the expire duration for a key to live. Until redigo supports
|
|
||||||
// sharding/clustering, only one host will be in hostList
|
|
||||||
//
|
|
||||||
// engine.SetDefaultCacher(xormrediscache.NewRedisCacher("localhost:6379", "", xormrediscache.DEFAULT_EXPIRATION, engine.Logger))
|
|
||||||
//
|
|
||||||
// or set MapCacher
|
|
||||||
//
|
|
||||||
// engine.MapCacher(&user, xormrediscache.NewRedisCacher("localhost:6379", "", xormrediscache.DEFAULT_EXPIRATION, engine.Logger))
|
|
||||||
//
|
|
||||||
func NewRedisCacher(host string, password string, defaultExpiration time.Duration, logger core.ILogger) *RedisCacher {
|
|
||||||
var pool = &redis.Pool{
|
|
||||||
MaxIdle: 5,
|
|
||||||
IdleTimeout: 240 * time.Second,
|
|
||||||
Dial: func() (redis.Conn, error) {
|
|
||||||
// the redis protocol should probably be made sett-able
|
|
||||||
c, err := redis.Dial("tcp", host)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(password) > 0 {
|
|
||||||
if _, err := c.Do("AUTH", password); err != nil {
|
|
||||||
c.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// check with PING
|
|
||||||
if _, err := c.Do("PING"); err != nil {
|
|
||||||
c.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return c, err
|
|
||||||
},
|
|
||||||
// custom connection test method
|
|
||||||
TestOnBorrow: func(c redis.Conn, t time.Time) error {
|
|
||||||
if _, err := c.Do("PING"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return &RedisCacher{pool: pool, defaultExpiration: defaultExpiration, Logger: logger}
|
|
||||||
}
|
|
||||||
|
|
||||||
func exists(conn redis.Conn, key string) bool {
|
|
||||||
existed, _ := redis.Bool(conn.Do("EXISTS", key))
|
|
||||||
return existed
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RedisCacher) logErrf(format string, contents ...interface{}) {
|
|
||||||
if c.Logger != nil {
|
|
||||||
c.Logger.Errorf(fmt.Sprintf("%s %s", LOGGING_PREFIX, format), contents...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RedisCacher) logDebugf(format string, contents ...interface{}) {
|
|
||||||
if c.Logger != nil {
|
|
||||||
c.Logger.Debugf(fmt.Sprintf("%s %s", LOGGING_PREFIX, format), contents...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RedisCacher) getBeanKey(tableName string, id string) string {
|
|
||||||
return fmt.Sprintf("xorm:bean:%s:%s", tableName, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RedisCacher) getSqlKey(tableName string, sql string) string {
|
|
||||||
// hash sql to minimize key length
|
|
||||||
crc := crc32.ChecksumIEEE([]byte(sql))
|
|
||||||
return fmt.Sprintf("xorm:sql:%s:%d", tableName, crc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete all xorm cached objects
|
|
||||||
func (c *RedisCacher) Flush() error {
|
|
||||||
// conn := c.pool.Get()
|
|
||||||
// defer conn.Close()
|
|
||||||
// _, err := conn.Do("FLUSHALL")
|
|
||||||
// return err
|
|
||||||
return c.delObject("xorm:*")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RedisCacher) getObject(key string) interface{} {
|
|
||||||
conn := c.pool.Get()
|
|
||||||
defer conn.Close()
|
|
||||||
raw, err := conn.Do("GET", key)
|
|
||||||
if raw == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
item, err := redis.Bytes(raw, err)
|
|
||||||
if err != nil {
|
|
||||||
c.logErrf("redis.Bytes failed: %s", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
value, err := c.deserialize(item)
|
|
||||||
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RedisCacher) GetIds(tableName, sql string) interface{} {
|
|
||||||
sqlKey := c.getSqlKey(tableName, sql)
|
|
||||||
c.logDebugf(" GetIds|tableName:%s|sql:%s|key:%s", tableName, sql, sqlKey)
|
|
||||||
return c.getObject(sqlKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RedisCacher) GetBean(tableName string, id string) interface{} {
|
|
||||||
beanKey := c.getBeanKey(tableName, id)
|
|
||||||
c.logDebugf("[xorm/redis_cacher] GetBean|tableName:%s|id:%s|key:%s", tableName, id, beanKey)
|
|
||||||
return c.getObject(beanKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RedisCacher) putObject(key string, value interface{}) {
|
|
||||||
c.invoke(c.pool.Get().Do, key, value, c.defaultExpiration)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RedisCacher) PutIds(tableName, sql string, ids interface{}) {
|
|
||||||
sqlKey := c.getSqlKey(tableName, sql)
|
|
||||||
c.logDebugf("PutIds|tableName:%s|sql:%s|key:%s|obj:%s|type:%v", tableName, sql, sqlKey, ids, reflect.TypeOf(ids))
|
|
||||||
c.putObject(sqlKey, ids)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RedisCacher) PutBean(tableName string, id string, obj interface{}) {
|
|
||||||
beanKey := c.getBeanKey(tableName, id)
|
|
||||||
c.logDebugf("PutBean|tableName:%s|id:%s|key:%s|type:%v", tableName, id, beanKey, reflect.TypeOf(obj))
|
|
||||||
c.putObject(beanKey, obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RedisCacher) delObject(key string) error {
|
|
||||||
c.logDebugf("delObject key:[%s]", key)
|
|
||||||
|
|
||||||
conn := c.pool.Get()
|
|
||||||
defer conn.Close()
|
|
||||||
if !exists(conn, key) {
|
|
||||||
c.logErrf("delObject key:[%s] err: %v", key, core.ErrCacheMiss)
|
|
||||||
return core.ErrCacheMiss
|
|
||||||
}
|
|
||||||
_, err := conn.Do("DEL", key)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RedisCacher) delObjects(key string) error {
|
|
||||||
|
|
||||||
c.logDebugf("delObjects key:[%s]", key)
|
|
||||||
|
|
||||||
conn := c.pool.Get()
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
keys, err := conn.Do("KEYS", key)
|
|
||||||
c.logDebugf("delObjects keys: %v", keys)
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
for _, key := range keys.([]interface{}) {
|
|
||||||
conn.Do("DEL", key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RedisCacher) DelIds(tableName, sql string) {
|
|
||||||
c.delObject(c.getSqlKey(tableName, sql))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RedisCacher) DelBean(tableName string, id string) {
|
|
||||||
c.delObject(c.getBeanKey(tableName, id))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RedisCacher) ClearIds(tableName string) {
|
|
||||||
c.delObjects(c.getSqlKey(tableName, "*"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RedisCacher) ClearBeans(tableName string) {
|
|
||||||
c.delObjects(c.getBeanKey(tableName, "*"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RedisCacher) invoke(f func(string, ...interface{}) (interface{}, error),
|
|
||||||
key string, value interface{}, expires time.Duration) error {
|
|
||||||
|
|
||||||
switch expires {
|
|
||||||
case DEFAULT_EXPIRATION:
|
|
||||||
expires = c.defaultExpiration
|
|
||||||
case FOREVER_EXPIRATION:
|
|
||||||
expires = time.Duration(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := c.serialize(value)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
conn := c.pool.Get()
|
|
||||||
defer conn.Close()
|
|
||||||
if expires > 0 {
|
|
||||||
_, err := f("SETEX", key, int32(expires/time.Second), b)
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
_, err := f("SET", key, b)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RedisCacher) serialize(value interface{}) ([]byte, error) {
|
|
||||||
|
|
||||||
err := c.registerGobConcreteType(value)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if reflect.TypeOf(value).Kind() == reflect.Struct {
|
|
||||||
return nil, fmt.Errorf("serialize func only take pointer of a struct")
|
|
||||||
}
|
|
||||||
|
|
||||||
var b bytes.Buffer
|
|
||||||
encoder := gob.NewEncoder(&b)
|
|
||||||
|
|
||||||
c.logDebugf("serialize type:%v", reflect.TypeOf(value))
|
|
||||||
err = encoder.Encode(&value)
|
|
||||||
if err != nil {
|
|
||||||
c.logErrf("gob encoding '%s' failed: %s|value:%v", value, err, value)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return b.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RedisCacher) deserialize(byt []byte) (ptr interface{}, err error) {
|
|
||||||
b := bytes.NewBuffer(byt)
|
|
||||||
decoder := gob.NewDecoder(b)
|
|
||||||
|
|
||||||
var p interface{}
|
|
||||||
err = decoder.Decode(&p)
|
|
||||||
if err != nil {
|
|
||||||
c.logErrf("decode failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
v := reflect.ValueOf(p)
|
|
||||||
c.logDebugf("deserialize type:%v", v.Type())
|
|
||||||
if v.Kind() == reflect.Struct {
|
|
||||||
|
|
||||||
var pp interface{} = &p
|
|
||||||
datas := reflect.ValueOf(pp).Elem().InterfaceData()
|
|
||||||
|
|
||||||
sp := reflect.NewAt(v.Type(),
|
|
||||||
unsafe.Pointer(datas[1])).Interface()
|
|
||||||
ptr = sp
|
|
||||||
vv := reflect.ValueOf(ptr)
|
|
||||||
c.logDebugf("deserialize convert ptr type:%v | CanAddr:%t", vv.Type(), vv.CanAddr())
|
|
||||||
} else {
|
|
||||||
ptr = p
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *RedisCacher) registerGobConcreteType(value interface{}) error {
|
|
||||||
|
|
||||||
t := reflect.TypeOf(value)
|
|
||||||
|
|
||||||
c.logDebugf("registerGobConcreteType:%v", t)
|
|
||||||
|
|
||||||
switch t.Kind() {
|
|
||||||
case reflect.Ptr:
|
|
||||||
v := reflect.ValueOf(value)
|
|
||||||
i := v.Elem().Interface()
|
|
||||||
gob.Register(i)
|
|
||||||
case reflect.Struct, reflect.Map, reflect.Slice:
|
|
||||||
gob.Register(value)
|
|
||||||
case reflect.String, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
|
|
||||||
// do nothing since already registered known type
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unhandled type: %v", t)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
6
vendor/github.com/go-xorm/xorm-redis-cache/run_test.sh
generated
vendored
6
vendor/github.com/go-xorm/xorm-redis-cache/run_test.sh
generated
vendored
|
@ -1,6 +0,0 @@
|
||||||
redis-cli FLUSHALL
|
|
||||||
if [ $? == "0" ];then
|
|
||||||
go test -v -run=TestMysqlWithCache
|
|
||||||
else
|
|
||||||
echo "no redis-server running on localhost"
|
|
||||||
fi
|
|
30
vendor/github.com/go-xorm/xorm/.gitignore
generated
vendored
Normal file
30
vendor/github.com/go-xorm/xorm/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
*.db
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
|
||||||
|
*.log
|
||||||
|
.vendor
|
||||||
|
temp_test.go
|
||||||
|
.vscode
|
||||||
|
xorm.test
|
||||||
|
*.sqlite3
|
0
vendor/github.com/go-xorm/xorm/gen_reserved.sh
generated
vendored
Executable file → Normal file
0
vendor/github.com/go-xorm/xorm/gen_reserved.sh
generated
vendored
Executable file → Normal file
0
vendor/github.com/go-xorm/xorm/test_mssql.sh
generated
vendored
Executable file → Normal file
0
vendor/github.com/go-xorm/xorm/test_mssql.sh
generated
vendored
Executable file → Normal file
0
vendor/github.com/go-xorm/xorm/test_mssql_cache.sh
generated
vendored
Executable file → Normal file
0
vendor/github.com/go-xorm/xorm/test_mssql_cache.sh
generated
vendored
Executable file → Normal file
0
vendor/github.com/go-xorm/xorm/test_mymysql.sh
generated
vendored
Executable file → Normal file
0
vendor/github.com/go-xorm/xorm/test_mymysql.sh
generated
vendored
Executable file → Normal file
0
vendor/github.com/go-xorm/xorm/test_mymysql_cache.sh
generated
vendored
Executable file → Normal file
0
vendor/github.com/go-xorm/xorm/test_mymysql_cache.sh
generated
vendored
Executable file → Normal file
0
vendor/github.com/go-xorm/xorm/test_mysql.sh
generated
vendored
Executable file → Normal file
0
vendor/github.com/go-xorm/xorm/test_mysql.sh
generated
vendored
Executable file → Normal file
0
vendor/github.com/go-xorm/xorm/test_mysql_cache.sh
generated
vendored
Executable file → Normal file
0
vendor/github.com/go-xorm/xorm/test_mysql_cache.sh
generated
vendored
Executable file → Normal file
0
vendor/github.com/go-xorm/xorm/test_postgres.sh
generated
vendored
Executable file → Normal file
0
vendor/github.com/go-xorm/xorm/test_postgres.sh
generated
vendored
Executable file → Normal file
0
vendor/github.com/go-xorm/xorm/test_postgres_cache.sh
generated
vendored
Executable file → Normal file
0
vendor/github.com/go-xorm/xorm/test_postgres_cache.sh
generated
vendored
Executable file → Normal file
0
vendor/github.com/go-xorm/xorm/test_sqlite.sh
generated
vendored
Executable file → Normal file
0
vendor/github.com/go-xorm/xorm/test_sqlite.sh
generated
vendored
Executable file → Normal file
0
vendor/github.com/go-xorm/xorm/test_sqlite_cache.sh
generated
vendored
Executable file → Normal file
0
vendor/github.com/go-xorm/xorm/test_sqlite_cache.sh
generated
vendored
Executable file → Normal file
27
vendor/github.com/gorilla/context/LICENSE
generated
vendored
27
vendor/github.com/gorilla/context/LICENSE
generated
vendored
|
@ -1,27 +0,0 @@
|
||||||
Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
10
vendor/github.com/gorilla/context/README.md
generated
vendored
10
vendor/github.com/gorilla/context/README.md
generated
vendored
|
@ -1,10 +0,0 @@
|
||||||
context
|
|
||||||
=======
|
|
||||||
[![Build Status](https://travis-ci.org/gorilla/context.png?branch=master)](https://travis-ci.org/gorilla/context)
|
|
||||||
|
|
||||||
gorilla/context is a general purpose registry for global request variables.
|
|
||||||
|
|
||||||
> Note: gorilla/context, having been born well before `context.Context` existed, does not play well
|
|
||||||
> with the shallow copying of the request that [`http.Request.WithContext`](https://golang.org/pkg/net/http/#Request.WithContext) (added to net/http Go 1.7 onwards) performs. You should either use *just* gorilla/context, or moving forward, the new `http.Request.Context()`.
|
|
||||||
|
|
||||||
Read the full documentation here: http://www.gorillatoolkit.org/pkg/context
|
|
143
vendor/github.com/gorilla/context/context.go
generated
vendored
143
vendor/github.com/gorilla/context/context.go
generated
vendored
|
@ -1,143 +0,0 @@
|
||||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package context
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
mutex sync.RWMutex
|
|
||||||
data = make(map[*http.Request]map[interface{}]interface{})
|
|
||||||
datat = make(map[*http.Request]int64)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Set stores a value for a given key in a given request.
|
|
||||||
func Set(r *http.Request, key, val interface{}) {
|
|
||||||
mutex.Lock()
|
|
||||||
if data[r] == nil {
|
|
||||||
data[r] = make(map[interface{}]interface{})
|
|
||||||
datat[r] = time.Now().Unix()
|
|
||||||
}
|
|
||||||
data[r][key] = val
|
|
||||||
mutex.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns a value stored for a given key in a given request.
|
|
||||||
func Get(r *http.Request, key interface{}) interface{} {
|
|
||||||
mutex.RLock()
|
|
||||||
if ctx := data[r]; ctx != nil {
|
|
||||||
value := ctx[key]
|
|
||||||
mutex.RUnlock()
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
mutex.RUnlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetOk returns stored value and presence state like multi-value return of map access.
|
|
||||||
func GetOk(r *http.Request, key interface{}) (interface{}, bool) {
|
|
||||||
mutex.RLock()
|
|
||||||
if _, ok := data[r]; ok {
|
|
||||||
value, ok := data[r][key]
|
|
||||||
mutex.RUnlock()
|
|
||||||
return value, ok
|
|
||||||
}
|
|
||||||
mutex.RUnlock()
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAll returns all stored values for the request as a map. Nil is returned for invalid requests.
|
|
||||||
func GetAll(r *http.Request) map[interface{}]interface{} {
|
|
||||||
mutex.RLock()
|
|
||||||
if context, ok := data[r]; ok {
|
|
||||||
result := make(map[interface{}]interface{}, len(context))
|
|
||||||
for k, v := range context {
|
|
||||||
result[k] = v
|
|
||||||
}
|
|
||||||
mutex.RUnlock()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
mutex.RUnlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAllOk returns all stored values for the request as a map and a boolean value that indicates if
|
|
||||||
// the request was registered.
|
|
||||||
func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool) {
|
|
||||||
mutex.RLock()
|
|
||||||
context, ok := data[r]
|
|
||||||
result := make(map[interface{}]interface{}, len(context))
|
|
||||||
for k, v := range context {
|
|
||||||
result[k] = v
|
|
||||||
}
|
|
||||||
mutex.RUnlock()
|
|
||||||
return result, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete removes a value stored for a given key in a given request.
|
|
||||||
func Delete(r *http.Request, key interface{}) {
|
|
||||||
mutex.Lock()
|
|
||||||
if data[r] != nil {
|
|
||||||
delete(data[r], key)
|
|
||||||
}
|
|
||||||
mutex.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear removes all values stored for a given request.
|
|
||||||
//
|
|
||||||
// This is usually called by a handler wrapper to clean up request
|
|
||||||
// variables at the end of a request lifetime. See ClearHandler().
|
|
||||||
func Clear(r *http.Request) {
|
|
||||||
mutex.Lock()
|
|
||||||
clear(r)
|
|
||||||
mutex.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear is Clear without the lock.
|
|
||||||
func clear(r *http.Request) {
|
|
||||||
delete(data, r)
|
|
||||||
delete(datat, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Purge removes request data stored for longer than maxAge, in seconds.
|
|
||||||
// It returns the amount of requests removed.
|
|
||||||
//
|
|
||||||
// If maxAge <= 0, all request data is removed.
|
|
||||||
//
|
|
||||||
// This is only used for sanity check: in case context cleaning was not
|
|
||||||
// properly set some request data can be kept forever, consuming an increasing
|
|
||||||
// amount of memory. In case this is detected, Purge() must be called
|
|
||||||
// periodically until the problem is fixed.
|
|
||||||
func Purge(maxAge int) int {
|
|
||||||
mutex.Lock()
|
|
||||||
count := 0
|
|
||||||
if maxAge <= 0 {
|
|
||||||
count = len(data)
|
|
||||||
data = make(map[*http.Request]map[interface{}]interface{})
|
|
||||||
datat = make(map[*http.Request]int64)
|
|
||||||
} else {
|
|
||||||
min := time.Now().Unix() - int64(maxAge)
|
|
||||||
for r := range data {
|
|
||||||
if datat[r] < min {
|
|
||||||
clear(r)
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mutex.Unlock()
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClearHandler wraps an http.Handler and clears request values at the end
|
|
||||||
// of a request lifetime.
|
|
||||||
func ClearHandler(h http.Handler) http.Handler {
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
defer Clear(r)
|
|
||||||
h.ServeHTTP(w, r)
|
|
||||||
})
|
|
||||||
}
|
|
88
vendor/github.com/gorilla/context/doc.go
generated
vendored
88
vendor/github.com/gorilla/context/doc.go
generated
vendored
|
@ -1,88 +0,0 @@
|
||||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package context stores values shared during a request lifetime.
|
|
||||||
|
|
||||||
Note: gorilla/context, having been born well before `context.Context` existed,
|
|
||||||
does not play well > with the shallow copying of the request that
|
|
||||||
[`http.Request.WithContext`](https://golang.org/pkg/net/http/#Request.WithContext)
|
|
||||||
(added to net/http Go 1.7 onwards) performs. You should either use *just*
|
|
||||||
gorilla/context, or moving forward, the new `http.Request.Context()`.
|
|
||||||
|
|
||||||
For example, a router can set variables extracted from the URL and later
|
|
||||||
application handlers can access those values, or it can be used to store
|
|
||||||
sessions values to be saved at the end of a request. There are several
|
|
||||||
others common uses.
|
|
||||||
|
|
||||||
The idea was posted by Brad Fitzpatrick to the go-nuts mailing list:
|
|
||||||
|
|
||||||
http://groups.google.com/group/golang-nuts/msg/e2d679d303aa5d53
|
|
||||||
|
|
||||||
Here's the basic usage: first define the keys that you will need. The key
|
|
||||||
type is interface{} so a key can be of any type that supports equality.
|
|
||||||
Here we define a key using a custom int type to avoid name collisions:
|
|
||||||
|
|
||||||
package foo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gorilla/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
type key int
|
|
||||||
|
|
||||||
const MyKey key = 0
|
|
||||||
|
|
||||||
Then set a variable. Variables are bound to an http.Request object, so you
|
|
||||||
need a request instance to set a value:
|
|
||||||
|
|
||||||
context.Set(r, MyKey, "bar")
|
|
||||||
|
|
||||||
The application can later access the variable using the same key you provided:
|
|
||||||
|
|
||||||
func MyHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// val is "bar".
|
|
||||||
val := context.Get(r, foo.MyKey)
|
|
||||||
|
|
||||||
// returns ("bar", true)
|
|
||||||
val, ok := context.GetOk(r, foo.MyKey)
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
And that's all about the basic usage. We discuss some other ideas below.
|
|
||||||
|
|
||||||
Any type can be stored in the context. To enforce a given type, make the key
|
|
||||||
private and wrap Get() and Set() to accept and return values of a specific
|
|
||||||
type:
|
|
||||||
|
|
||||||
type key int
|
|
||||||
|
|
||||||
const mykey key = 0
|
|
||||||
|
|
||||||
// GetMyKey returns a value for this package from the request values.
|
|
||||||
func GetMyKey(r *http.Request) SomeType {
|
|
||||||
if rv := context.Get(r, mykey); rv != nil {
|
|
||||||
return rv.(SomeType)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetMyKey sets a value for this package in the request values.
|
|
||||||
func SetMyKey(r *http.Request, val SomeType) {
|
|
||||||
context.Set(r, mykey, val)
|
|
||||||
}
|
|
||||||
|
|
||||||
Variables must be cleared at the end of a request, to remove all values
|
|
||||||
that were stored. This can be done in an http.Handler, after a request was
|
|
||||||
served. Just call Clear() passing the request:
|
|
||||||
|
|
||||||
context.Clear(r)
|
|
||||||
|
|
||||||
...or use ClearHandler(), which conveniently wraps an http.Handler to clear
|
|
||||||
variables at the end of a request lifetime.
|
|
||||||
|
|
||||||
The Routers from the packages gorilla/mux and gorilla/pat call Clear()
|
|
||||||
so if you are using either of them you don't need to clear the context manually.
|
|
||||||
*/
|
|
||||||
package context
|
|
27
vendor/github.com/gorilla/securecookie/LICENSE
generated
vendored
27
vendor/github.com/gorilla/securecookie/LICENSE
generated
vendored
|
@ -1,27 +0,0 @@
|
||||||
Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
80
vendor/github.com/gorilla/securecookie/README.md
generated
vendored
80
vendor/github.com/gorilla/securecookie/README.md
generated
vendored
|
@ -1,80 +0,0 @@
|
||||||
securecookie
|
|
||||||
============
|
|
||||||
[![GoDoc](https://godoc.org/github.com/gorilla/securecookie?status.svg)](https://godoc.org/github.com/gorilla/securecookie) [![Build Status](https://travis-ci.org/gorilla/securecookie.png?branch=master)](https://travis-ci.org/gorilla/securecookie)
|
|
||||||
[![Sourcegraph](https://sourcegraph.com/github.com/gorilla/securecookie/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/securecookie?badge)
|
|
||||||
|
|
||||||
|
|
||||||
securecookie encodes and decodes authenticated and optionally encrypted
|
|
||||||
cookie values.
|
|
||||||
|
|
||||||
Secure cookies can't be forged, because their values are validated using HMAC.
|
|
||||||
When encrypted, the content is also inaccessible to malicious eyes. It is still
|
|
||||||
recommended that sensitive data not be stored in cookies, and that HTTPS be used
|
|
||||||
to prevent cookie [replay attacks](https://en.wikipedia.org/wiki/Replay_attack).
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
To use it, first create a new SecureCookie instance:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Hash keys should be at least 32 bytes long
|
|
||||||
var hashKey = []byte("very-secret")
|
|
||||||
// Block keys should be 16 bytes (AES-128) or 32 bytes (AES-256) long.
|
|
||||||
// Shorter keys may weaken the encryption used.
|
|
||||||
var blockKey = []byte("a-lot-secret")
|
|
||||||
var s = securecookie.New(hashKey, blockKey)
|
|
||||||
```
|
|
||||||
|
|
||||||
The hashKey is required, used to authenticate the cookie value using HMAC.
|
|
||||||
It is recommended to use a key with 32 or 64 bytes.
|
|
||||||
|
|
||||||
The blockKey is optional, used to encrypt the cookie value -- set it to nil
|
|
||||||
to not use encryption. If set, the length must correspond to the block size
|
|
||||||
of the encryption algorithm. For AES, used by default, valid lengths are
|
|
||||||
16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.
|
|
||||||
|
|
||||||
Strong keys can be created using the convenience function GenerateRandomKey().
|
|
||||||
|
|
||||||
Once a SecureCookie instance is set, use it to encode a cookie value:
|
|
||||||
|
|
||||||
```go
|
|
||||||
func SetCookieHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
value := map[string]string{
|
|
||||||
"foo": "bar",
|
|
||||||
}
|
|
||||||
if encoded, err := s.Encode("cookie-name", value); err == nil {
|
|
||||||
cookie := &http.Cookie{
|
|
||||||
Name: "cookie-name",
|
|
||||||
Value: encoded,
|
|
||||||
Path: "/",
|
|
||||||
Secure: true,
|
|
||||||
HttpOnly: true,
|
|
||||||
}
|
|
||||||
http.SetCookie(w, cookie)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Later, use the same SecureCookie instance to decode and validate a cookie
|
|
||||||
value:
|
|
||||||
|
|
||||||
```go
|
|
||||||
func ReadCookieHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if cookie, err := r.Cookie("cookie-name"); err == nil {
|
|
||||||
value := make(map[string]string)
|
|
||||||
if err = s2.Decode("cookie-name", cookie.Value, &value); err == nil {
|
|
||||||
fmt.Fprintf(w, "The value of foo is %q", value["foo"])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
We stored a map[string]string, but secure cookies can hold any value that
|
|
||||||
can be encoded using `encoding/gob`. To store custom types, they must be
|
|
||||||
registered first using gob.Register(). For basic types this is not needed;
|
|
||||||
it works out of the box. An optional JSON encoder that uses `encoding/json` is
|
|
||||||
available for types compatible with JSON.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
BSD licensed. See the LICENSE file for details.
|
|
61
vendor/github.com/gorilla/securecookie/doc.go
generated
vendored
61
vendor/github.com/gorilla/securecookie/doc.go
generated
vendored
|
@ -1,61 +0,0 @@
|
||||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package securecookie encodes and decodes authenticated and optionally
|
|
||||||
encrypted cookie values.
|
|
||||||
|
|
||||||
Secure cookies can't be forged, because their values are validated using HMAC.
|
|
||||||
When encrypted, the content is also inaccessible to malicious eyes.
|
|
||||||
|
|
||||||
To use it, first create a new SecureCookie instance:
|
|
||||||
|
|
||||||
var hashKey = []byte("very-secret")
|
|
||||||
var blockKey = []byte("a-lot-secret")
|
|
||||||
var s = securecookie.New(hashKey, blockKey)
|
|
||||||
|
|
||||||
The hashKey is required, used to authenticate the cookie value using HMAC.
|
|
||||||
It is recommended to use a key with 32 or 64 bytes.
|
|
||||||
|
|
||||||
The blockKey is optional, used to encrypt the cookie value -- set it to nil
|
|
||||||
to not use encryption. If set, the length must correspond to the block size
|
|
||||||
of the encryption algorithm. For AES, used by default, valid lengths are
|
|
||||||
16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.
|
|
||||||
|
|
||||||
Strong keys can be created using the convenience function GenerateRandomKey().
|
|
||||||
|
|
||||||
Once a SecureCookie instance is set, use it to encode a cookie value:
|
|
||||||
|
|
||||||
func SetCookieHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
value := map[string]string{
|
|
||||||
"foo": "bar",
|
|
||||||
}
|
|
||||||
if encoded, err := s.Encode("cookie-name", value); err == nil {
|
|
||||||
cookie := &http.Cookie{
|
|
||||||
Name: "cookie-name",
|
|
||||||
Value: encoded,
|
|
||||||
Path: "/",
|
|
||||||
}
|
|
||||||
http.SetCookie(w, cookie)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Later, use the same SecureCookie instance to decode and validate a cookie
|
|
||||||
value:
|
|
||||||
|
|
||||||
func ReadCookieHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if cookie, err := r.Cookie("cookie-name"); err == nil {
|
|
||||||
value := make(map[string]string)
|
|
||||||
if err = s2.Decode("cookie-name", cookie.Value, &value); err == nil {
|
|
||||||
fmt.Fprintf(w, "The value of foo is %q", value["foo"])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
We stored a map[string]string, but secure cookies can hold any value that
|
|
||||||
can be encoded using encoding/gob. To store custom types, they must be
|
|
||||||
registered first using gob.Register(). For basic types this is not needed;
|
|
||||||
it works out of the box.
|
|
||||||
*/
|
|
||||||
package securecookie
|
|
25
vendor/github.com/gorilla/securecookie/fuzz.go
generated
vendored
25
vendor/github.com/gorilla/securecookie/fuzz.go
generated
vendored
|
@ -1,25 +0,0 @@
|
||||||
// +build gofuzz
|
|
||||||
|
|
||||||
package securecookie
|
|
||||||
|
|
||||||
var hashKey = []byte("very-secret12345")
|
|
||||||
var blockKey = []byte("a-lot-secret1234")
|
|
||||||
var s = New(hashKey, blockKey)
|
|
||||||
|
|
||||||
type Cookie struct {
|
|
||||||
B bool
|
|
||||||
I int
|
|
||||||
S string
|
|
||||||
}
|
|
||||||
|
|
||||||
func Fuzz(data []byte) int {
|
|
||||||
datas := string(data)
|
|
||||||
var c Cookie
|
|
||||||
if err := s.Decode("fuzz", datas, &c); err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if _, err := s.Encode("fuzz", c); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
646
vendor/github.com/gorilla/securecookie/securecookie.go
generated
vendored
646
vendor/github.com/gorilla/securecookie/securecookie.go
generated
vendored
|
@ -1,646 +0,0 @@
|
||||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package securecookie
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/aes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/subtle"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/gob"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"hash"
|
|
||||||
"io"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Error is the interface of all errors returned by functions in this library.
|
|
||||||
type Error interface {
|
|
||||||
error
|
|
||||||
|
|
||||||
// IsUsage returns true for errors indicating the client code probably
|
|
||||||
// uses this library incorrectly. For example, the client may have
|
|
||||||
// failed to provide a valid hash key, or may have failed to configure
|
|
||||||
// the Serializer adequately for encoding value.
|
|
||||||
IsUsage() bool
|
|
||||||
|
|
||||||
// IsDecode returns true for errors indicating that a cookie could not
|
|
||||||
// be decoded and validated. Since cookies are usually untrusted
|
|
||||||
// user-provided input, errors of this type should be expected.
|
|
||||||
// Usually, the proper action is simply to reject the request.
|
|
||||||
IsDecode() bool
|
|
||||||
|
|
||||||
// IsInternal returns true for unexpected errors occurring in the
|
|
||||||
// securecookie implementation.
|
|
||||||
IsInternal() bool
|
|
||||||
|
|
||||||
// Cause, if it returns a non-nil value, indicates that this error was
|
|
||||||
// propagated from some underlying library. If this method returns nil,
|
|
||||||
// this error was raised directly by this library.
|
|
||||||
//
|
|
||||||
// Cause is provided principally for debugging/logging purposes; it is
|
|
||||||
// rare that application logic should perform meaningfully different
|
|
||||||
// logic based on Cause. See, for example, the caveats described on
|
|
||||||
// (MultiError).Cause().
|
|
||||||
Cause() error
|
|
||||||
}
|
|
||||||
|
|
||||||
// errorType is a bitmask giving the error type(s) of an cookieError value.
|
|
||||||
type errorType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
usageError = errorType(1 << iota)
|
|
||||||
decodeError
|
|
||||||
internalError
|
|
||||||
)
|
|
||||||
|
|
||||||
type cookieError struct {
|
|
||||||
typ errorType
|
|
||||||
msg string
|
|
||||||
cause error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e cookieError) IsUsage() bool { return (e.typ & usageError) != 0 }
|
|
||||||
func (e cookieError) IsDecode() bool { return (e.typ & decodeError) != 0 }
|
|
||||||
func (e cookieError) IsInternal() bool { return (e.typ & internalError) != 0 }
|
|
||||||
|
|
||||||
func (e cookieError) Cause() error { return e.cause }
|
|
||||||
|
|
||||||
func (e cookieError) Error() string {
|
|
||||||
parts := []string{"securecookie: "}
|
|
||||||
if e.msg == "" {
|
|
||||||
parts = append(parts, "error")
|
|
||||||
} else {
|
|
||||||
parts = append(parts, e.msg)
|
|
||||||
}
|
|
||||||
if c := e.Cause(); c != nil {
|
|
||||||
parts = append(parts, " - caused by: ", c.Error())
|
|
||||||
}
|
|
||||||
return strings.Join(parts, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
errGeneratingIV = cookieError{typ: internalError, msg: "failed to generate random iv"}
|
|
||||||
|
|
||||||
errNoCodecs = cookieError{typ: usageError, msg: "no codecs provided"}
|
|
||||||
errHashKeyNotSet = cookieError{typ: usageError, msg: "hash key is not set"}
|
|
||||||
errBlockKeyNotSet = cookieError{typ: usageError, msg: "block key is not set"}
|
|
||||||
errEncodedValueTooLong = cookieError{typ: usageError, msg: "the value is too long"}
|
|
||||||
|
|
||||||
errValueToDecodeTooLong = cookieError{typ: decodeError, msg: "the value is too long"}
|
|
||||||
errTimestampInvalid = cookieError{typ: decodeError, msg: "invalid timestamp"}
|
|
||||||
errTimestampTooNew = cookieError{typ: decodeError, msg: "timestamp is too new"}
|
|
||||||
errTimestampExpired = cookieError{typ: decodeError, msg: "expired timestamp"}
|
|
||||||
errDecryptionFailed = cookieError{typ: decodeError, msg: "the value could not be decrypted"}
|
|
||||||
errValueNotByte = cookieError{typ: decodeError, msg: "value not a []byte."}
|
|
||||||
errValueNotBytePtr = cookieError{typ: decodeError, msg: "value not a pointer to []byte."}
|
|
||||||
|
|
||||||
// ErrMacInvalid indicates that cookie decoding failed because the HMAC
|
|
||||||
// could not be extracted and verified. Direct use of this error
|
|
||||||
// variable is deprecated; it is public only for legacy compatibility,
|
|
||||||
// and may be privatized in the future, as it is rarely useful to
|
|
||||||
// distinguish between this error and other Error implementations.
|
|
||||||
ErrMacInvalid = cookieError{typ: decodeError, msg: "the value is not valid"}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Codec defines an interface to encode and decode cookie values.
|
|
||||||
type Codec interface {
|
|
||||||
Encode(name string, value interface{}) (string, error)
|
|
||||||
Decode(name, value string, dst interface{}) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// New returns a new SecureCookie.
|
|
||||||
//
|
|
||||||
// hashKey is required, used to authenticate values using HMAC. Create it using
|
|
||||||
// GenerateRandomKey(). It is recommended to use a key with 32 or 64 bytes.
|
|
||||||
//
|
|
||||||
// blockKey is optional, used to encrypt values. Create it using
|
|
||||||
// GenerateRandomKey(). The key length must correspond to the block size
|
|
||||||
// of the encryption algorithm. For AES, used by default, valid lengths are
|
|
||||||
// 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.
|
|
||||||
// The default encoder used for cookie serialization is encoding/gob.
|
|
||||||
//
|
|
||||||
// Note that keys created using GenerateRandomKey() are not automatically
|
|
||||||
// persisted. New keys will be created when the application is restarted, and
|
|
||||||
// previously issued cookies will not be able to be decoded.
|
|
||||||
func New(hashKey, blockKey []byte) *SecureCookie {
|
|
||||||
s := &SecureCookie{
|
|
||||||
hashKey: hashKey,
|
|
||||||
blockKey: blockKey,
|
|
||||||
hashFunc: sha256.New,
|
|
||||||
maxAge: 86400 * 30,
|
|
||||||
maxLength: 4096,
|
|
||||||
sz: GobEncoder{},
|
|
||||||
}
|
|
||||||
if hashKey == nil {
|
|
||||||
s.err = errHashKeyNotSet
|
|
||||||
}
|
|
||||||
if blockKey != nil {
|
|
||||||
s.BlockFunc(aes.NewCipher)
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// SecureCookie encodes and decodes authenticated and optionally encrypted
|
|
||||||
// cookie values.
|
|
||||||
type SecureCookie struct {
|
|
||||||
hashKey []byte
|
|
||||||
hashFunc func() hash.Hash
|
|
||||||
blockKey []byte
|
|
||||||
block cipher.Block
|
|
||||||
maxLength int
|
|
||||||
maxAge int64
|
|
||||||
minAge int64
|
|
||||||
err error
|
|
||||||
sz Serializer
|
|
||||||
// For testing purposes, the function that returns the current timestamp.
|
|
||||||
// If not set, it will use time.Now().UTC().Unix().
|
|
||||||
timeFunc func() int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serializer provides an interface for providing custom serializers for cookie
|
|
||||||
// values.
|
|
||||||
type Serializer interface {
|
|
||||||
Serialize(src interface{}) ([]byte, error)
|
|
||||||
Deserialize(src []byte, dst interface{}) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// GobEncoder encodes cookie values using encoding/gob. This is the simplest
|
|
||||||
// encoder and can handle complex types via gob.Register.
|
|
||||||
type GobEncoder struct{}
|
|
||||||
|
|
||||||
// JSONEncoder encodes cookie values using encoding/json. Users who wish to
|
|
||||||
// encode complex types need to satisfy the json.Marshaller and
|
|
||||||
// json.Unmarshaller interfaces.
|
|
||||||
type JSONEncoder struct{}
|
|
||||||
|
|
||||||
// NopEncoder does not encode cookie values, and instead simply accepts a []byte
|
|
||||||
// (as an interface{}) and returns a []byte. This is particularly useful when
|
|
||||||
// you encoding an object upstream and do not wish to re-encode it.
|
|
||||||
type NopEncoder struct{}
|
|
||||||
|
|
||||||
// MaxLength restricts the maximum length, in bytes, for the cookie value.
|
|
||||||
//
|
|
||||||
// Default is 4096, which is the maximum value accepted by Internet Explorer.
|
|
||||||
func (s *SecureCookie) MaxLength(value int) *SecureCookie {
|
|
||||||
s.maxLength = value
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// MaxAge restricts the maximum age, in seconds, for the cookie value.
|
|
||||||
//
|
|
||||||
// Default is 86400 * 30. Set it to 0 for no restriction.
|
|
||||||
func (s *SecureCookie) MaxAge(value int) *SecureCookie {
|
|
||||||
s.maxAge = int64(value)
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// MinAge restricts the minimum age, in seconds, for the cookie value.
|
|
||||||
//
|
|
||||||
// Default is 0 (no restriction).
|
|
||||||
func (s *SecureCookie) MinAge(value int) *SecureCookie {
|
|
||||||
s.minAge = int64(value)
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// HashFunc sets the hash function used to create HMAC.
|
|
||||||
//
|
|
||||||
// Default is crypto/sha256.New.
|
|
||||||
func (s *SecureCookie) HashFunc(f func() hash.Hash) *SecureCookie {
|
|
||||||
s.hashFunc = f
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlockFunc sets the encryption function used to create a cipher.Block.
|
|
||||||
//
|
|
||||||
// Default is crypto/aes.New.
|
|
||||||
func (s *SecureCookie) BlockFunc(f func([]byte) (cipher.Block, error)) *SecureCookie {
|
|
||||||
if s.blockKey == nil {
|
|
||||||
s.err = errBlockKeyNotSet
|
|
||||||
} else if block, err := f(s.blockKey); err == nil {
|
|
||||||
s.block = block
|
|
||||||
} else {
|
|
||||||
s.err = cookieError{cause: err, typ: usageError}
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encoding sets the encoding/serialization method for cookies.
|
|
||||||
//
|
|
||||||
// Default is encoding/gob. To encode special structures using encoding/gob,
|
|
||||||
// they must be registered first using gob.Register().
|
|
||||||
func (s *SecureCookie) SetSerializer(sz Serializer) *SecureCookie {
|
|
||||||
s.sz = sz
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode encodes a cookie value.
|
|
||||||
//
|
|
||||||
// It serializes, optionally encrypts, signs with a message authentication code,
|
|
||||||
// and finally encodes the value.
|
|
||||||
//
|
|
||||||
// The name argument is the cookie name. It is stored with the encoded value.
|
|
||||||
// The value argument is the value to be encoded. It can be any value that can
|
|
||||||
// be encoded using the currently selected serializer; see SetSerializer().
|
|
||||||
//
|
|
||||||
// It is the client's responsibility to ensure that value, when encoded using
|
|
||||||
// the current serialization/encryption settings on s and then base64-encoded,
|
|
||||||
// is shorter than the maximum permissible length.
|
|
||||||
func (s *SecureCookie) Encode(name string, value interface{}) (string, error) {
|
|
||||||
if s.err != nil {
|
|
||||||
return "", s.err
|
|
||||||
}
|
|
||||||
if s.hashKey == nil {
|
|
||||||
s.err = errHashKeyNotSet
|
|
||||||
return "", s.err
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
var b []byte
|
|
||||||
// 1. Serialize.
|
|
||||||
if b, err = s.sz.Serialize(value); err != nil {
|
|
||||||
return "", cookieError{cause: err, typ: usageError}
|
|
||||||
}
|
|
||||||
// 2. Encrypt (optional).
|
|
||||||
if s.block != nil {
|
|
||||||
if b, err = encrypt(s.block, b); err != nil {
|
|
||||||
return "", cookieError{cause: err, typ: usageError}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b = encode(b)
|
|
||||||
// 3. Create MAC for "name|date|value". Extra pipe to be used later.
|
|
||||||
b = []byte(fmt.Sprintf("%s|%d|%s|", name, s.timestamp(), b))
|
|
||||||
mac := createMac(hmac.New(s.hashFunc, s.hashKey), b[:len(b)-1])
|
|
||||||
// Append mac, remove name.
|
|
||||||
b = append(b, mac...)[len(name)+1:]
|
|
||||||
// 4. Encode to base64.
|
|
||||||
b = encode(b)
|
|
||||||
// 5. Check length.
|
|
||||||
if s.maxLength != 0 && len(b) > s.maxLength {
|
|
||||||
return "", errEncodedValueTooLong
|
|
||||||
}
|
|
||||||
// Done.
|
|
||||||
return string(b), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode decodes a cookie value.
|
|
||||||
//
|
|
||||||
// It decodes, verifies a message authentication code, optionally decrypts and
|
|
||||||
// finally deserializes the value.
|
|
||||||
//
|
|
||||||
// The name argument is the cookie name. It must be the same name used when
|
|
||||||
// it was stored. The value argument is the encoded cookie value. The dst
|
|
||||||
// argument is where the cookie will be decoded. It must be a pointer.
|
|
||||||
func (s *SecureCookie) Decode(name, value string, dst interface{}) error {
|
|
||||||
if s.err != nil {
|
|
||||||
return s.err
|
|
||||||
}
|
|
||||||
if s.hashKey == nil {
|
|
||||||
s.err = errHashKeyNotSet
|
|
||||||
return s.err
|
|
||||||
}
|
|
||||||
// 1. Check length.
|
|
||||||
if s.maxLength != 0 && len(value) > s.maxLength {
|
|
||||||
return errValueToDecodeTooLong
|
|
||||||
}
|
|
||||||
// 2. Decode from base64.
|
|
||||||
b, err := decode([]byte(value))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// 3. Verify MAC. Value is "date|value|mac".
|
|
||||||
parts := bytes.SplitN(b, []byte("|"), 3)
|
|
||||||
if len(parts) != 3 {
|
|
||||||
return ErrMacInvalid
|
|
||||||
}
|
|
||||||
h := hmac.New(s.hashFunc, s.hashKey)
|
|
||||||
b = append([]byte(name+"|"), b[:len(b)-len(parts[2])-1]...)
|
|
||||||
if err = verifyMac(h, b, parts[2]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// 4. Verify date ranges.
|
|
||||||
var t1 int64
|
|
||||||
if t1, err = strconv.ParseInt(string(parts[0]), 10, 64); err != nil {
|
|
||||||
return errTimestampInvalid
|
|
||||||
}
|
|
||||||
t2 := s.timestamp()
|
|
||||||
if s.minAge != 0 && t1 > t2-s.minAge {
|
|
||||||
return errTimestampTooNew
|
|
||||||
}
|
|
||||||
if s.maxAge != 0 && t1 < t2-s.maxAge {
|
|
||||||
return errTimestampExpired
|
|
||||||
}
|
|
||||||
// 5. Decrypt (optional).
|
|
||||||
b, err = decode(parts[1])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if s.block != nil {
|
|
||||||
if b, err = decrypt(s.block, b); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 6. Deserialize.
|
|
||||||
if err = s.sz.Deserialize(b, dst); err != nil {
|
|
||||||
return cookieError{cause: err, typ: decodeError}
|
|
||||||
}
|
|
||||||
// Done.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// timestamp returns the current timestamp, in seconds.
|
|
||||||
//
|
|
||||||
// For testing purposes, the function that generates the timestamp can be
|
|
||||||
// overridden. If not set, it will return time.Now().UTC().Unix().
|
|
||||||
func (s *SecureCookie) timestamp() int64 {
|
|
||||||
if s.timeFunc == nil {
|
|
||||||
return time.Now().UTC().Unix()
|
|
||||||
}
|
|
||||||
return s.timeFunc()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Authentication -------------------------------------------------------------
|
|
||||||
|
|
||||||
// createMac creates a message authentication code (MAC).
|
|
||||||
func createMac(h hash.Hash, value []byte) []byte {
|
|
||||||
h.Write(value)
|
|
||||||
return h.Sum(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// verifyMac verifies that a message authentication code (MAC) is valid.
|
|
||||||
func verifyMac(h hash.Hash, value []byte, mac []byte) error {
|
|
||||||
mac2 := createMac(h, value)
|
|
||||||
// Check that both MACs are of equal length, as subtle.ConstantTimeCompare
|
|
||||||
// does not do this prior to Go 1.4.
|
|
||||||
if len(mac) == len(mac2) && subtle.ConstantTimeCompare(mac, mac2) == 1 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return ErrMacInvalid
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encryption -----------------------------------------------------------------
|
|
||||||
|
|
||||||
// encrypt encrypts a value using the given block in counter mode.
|
|
||||||
//
|
|
||||||
// A random initialization vector (http://goo.gl/zF67k) with the length of the
|
|
||||||
// block size is prepended to the resulting ciphertext.
|
|
||||||
func encrypt(block cipher.Block, value []byte) ([]byte, error) {
|
|
||||||
iv := GenerateRandomKey(block.BlockSize())
|
|
||||||
if iv == nil {
|
|
||||||
return nil, errGeneratingIV
|
|
||||||
}
|
|
||||||
// Encrypt it.
|
|
||||||
stream := cipher.NewCTR(block, iv)
|
|
||||||
stream.XORKeyStream(value, value)
|
|
||||||
// Return iv + ciphertext.
|
|
||||||
return append(iv, value...), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// decrypt decrypts a value using the given block in counter mode.
|
|
||||||
//
|
|
||||||
// The value to be decrypted must be prepended by a initialization vector
|
|
||||||
// (http://goo.gl/zF67k) with the length of the block size.
|
|
||||||
func decrypt(block cipher.Block, value []byte) ([]byte, error) {
|
|
||||||
size := block.BlockSize()
|
|
||||||
if len(value) > size {
|
|
||||||
// Extract iv.
|
|
||||||
iv := value[:size]
|
|
||||||
// Extract ciphertext.
|
|
||||||
value = value[size:]
|
|
||||||
// Decrypt it.
|
|
||||||
stream := cipher.NewCTR(block, iv)
|
|
||||||
stream.XORKeyStream(value, value)
|
|
||||||
return value, nil
|
|
||||||
}
|
|
||||||
return nil, errDecryptionFailed
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialization --------------------------------------------------------------
|
|
||||||
|
|
||||||
// Serialize encodes a value using gob.
|
|
||||||
func (e GobEncoder) Serialize(src interface{}) ([]byte, error) {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
enc := gob.NewEncoder(buf)
|
|
||||||
if err := enc.Encode(src); err != nil {
|
|
||||||
return nil, cookieError{cause: err, typ: usageError}
|
|
||||||
}
|
|
||||||
return buf.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deserialize decodes a value using gob.
|
|
||||||
func (e GobEncoder) Deserialize(src []byte, dst interface{}) error {
|
|
||||||
dec := gob.NewDecoder(bytes.NewBuffer(src))
|
|
||||||
if err := dec.Decode(dst); err != nil {
|
|
||||||
return cookieError{cause: err, typ: decodeError}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize encodes a value using encoding/json.
|
|
||||||
func (e JSONEncoder) Serialize(src interface{}) ([]byte, error) {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
enc := json.NewEncoder(buf)
|
|
||||||
if err := enc.Encode(src); err != nil {
|
|
||||||
return nil, cookieError{cause: err, typ: usageError}
|
|
||||||
}
|
|
||||||
return buf.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deserialize decodes a value using encoding/json.
|
|
||||||
func (e JSONEncoder) Deserialize(src []byte, dst interface{}) error {
|
|
||||||
dec := json.NewDecoder(bytes.NewReader(src))
|
|
||||||
if err := dec.Decode(dst); err != nil {
|
|
||||||
return cookieError{cause: err, typ: decodeError}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize passes a []byte through as-is.
|
|
||||||
func (e NopEncoder) Serialize(src interface{}) ([]byte, error) {
|
|
||||||
if b, ok := src.([]byte); ok {
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, errValueNotByte
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deserialize passes a []byte through as-is.
|
|
||||||
func (e NopEncoder) Deserialize(src []byte, dst interface{}) error {
|
|
||||||
if dat, ok := dst.(*[]byte); ok {
|
|
||||||
*dat = src
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return errValueNotBytePtr
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encoding -------------------------------------------------------------------
|
|
||||||
|
|
||||||
// encode encodes a value using base64.
|
|
||||||
func encode(value []byte) []byte {
|
|
||||||
encoded := make([]byte, base64.URLEncoding.EncodedLen(len(value)))
|
|
||||||
base64.URLEncoding.Encode(encoded, value)
|
|
||||||
return encoded
|
|
||||||
}
|
|
||||||
|
|
||||||
// decode decodes a cookie using base64.
|
|
||||||
func decode(value []byte) ([]byte, error) {
|
|
||||||
decoded := make([]byte, base64.URLEncoding.DecodedLen(len(value)))
|
|
||||||
b, err := base64.URLEncoding.Decode(decoded, value)
|
|
||||||
if err != nil {
|
|
||||||
return nil, cookieError{cause: err, typ: decodeError, msg: "base64 decode failed"}
|
|
||||||
}
|
|
||||||
return decoded[:b], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helpers --------------------------------------------------------------------
|
|
||||||
|
|
||||||
// GenerateRandomKey creates a random key with the given length in bytes.
|
|
||||||
// On failure, returns nil.
|
|
||||||
//
|
|
||||||
// Callers should explicitly check for the possibility of a nil return, treat
|
|
||||||
// it as a failure of the system random number generator, and not continue.
|
|
||||||
func GenerateRandomKey(length int) []byte {
|
|
||||||
k := make([]byte, length)
|
|
||||||
if _, err := io.ReadFull(rand.Reader, k); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return k
|
|
||||||
}
|
|
||||||
|
|
||||||
// CodecsFromPairs returns a slice of SecureCookie instances.
|
|
||||||
//
|
|
||||||
// It is a convenience function to create a list of codecs for key rotation. Note
|
|
||||||
// that the generated Codecs will have the default options applied: callers
|
|
||||||
// should iterate over each Codec and type-assert the underlying *SecureCookie to
|
|
||||||
// change these.
|
|
||||||
//
|
|
||||||
// Example:
|
|
||||||
//
|
|
||||||
// codecs := securecookie.CodecsFromPairs(
|
|
||||||
// []byte("new-hash-key"),
|
|
||||||
// []byte("new-block-key"),
|
|
||||||
// []byte("old-hash-key"),
|
|
||||||
// []byte("old-block-key"),
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// // Modify each instance.
|
|
||||||
// for _, s := range codecs {
|
|
||||||
// if cookie, ok := s.(*securecookie.SecureCookie); ok {
|
|
||||||
// cookie.MaxAge(86400 * 7)
|
|
||||||
// cookie.SetSerializer(securecookie.JSONEncoder{})
|
|
||||||
// cookie.HashFunc(sha512.New512_256)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
func CodecsFromPairs(keyPairs ...[]byte) []Codec {
|
|
||||||
codecs := make([]Codec, len(keyPairs)/2+len(keyPairs)%2)
|
|
||||||
for i := 0; i < len(keyPairs); i += 2 {
|
|
||||||
var blockKey []byte
|
|
||||||
if i+1 < len(keyPairs) {
|
|
||||||
blockKey = keyPairs[i+1]
|
|
||||||
}
|
|
||||||
codecs[i/2] = New(keyPairs[i], blockKey)
|
|
||||||
}
|
|
||||||
return codecs
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeMulti encodes a cookie value using a group of codecs.
|
|
||||||
//
|
|
||||||
// The codecs are tried in order. Multiple codecs are accepted to allow
|
|
||||||
// key rotation.
|
|
||||||
//
|
|
||||||
// On error, may return a MultiError.
|
|
||||||
func EncodeMulti(name string, value interface{}, codecs ...Codec) (string, error) {
|
|
||||||
if len(codecs) == 0 {
|
|
||||||
return "", errNoCodecs
|
|
||||||
}
|
|
||||||
|
|
||||||
var errors MultiError
|
|
||||||
for _, codec := range codecs {
|
|
||||||
encoded, err := codec.Encode(name, value)
|
|
||||||
if err == nil {
|
|
||||||
return encoded, nil
|
|
||||||
}
|
|
||||||
errors = append(errors, err)
|
|
||||||
}
|
|
||||||
return "", errors
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeMulti decodes a cookie value using a group of codecs.
|
|
||||||
//
|
|
||||||
// The codecs are tried in order. Multiple codecs are accepted to allow
|
|
||||||
// key rotation.
|
|
||||||
//
|
|
||||||
// On error, may return a MultiError.
|
|
||||||
func DecodeMulti(name string, value string, dst interface{}, codecs ...Codec) error {
|
|
||||||
if len(codecs) == 0 {
|
|
||||||
return errNoCodecs
|
|
||||||
}
|
|
||||||
|
|
||||||
var errors MultiError
|
|
||||||
for _, codec := range codecs {
|
|
||||||
err := codec.Decode(name, value, dst)
|
|
||||||
if err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
errors = append(errors, err)
|
|
||||||
}
|
|
||||||
return errors
|
|
||||||
}
|
|
||||||
|
|
||||||
// MultiError groups multiple errors.
|
|
||||||
type MultiError []error
|
|
||||||
|
|
||||||
func (m MultiError) IsUsage() bool { return m.any(func(e Error) bool { return e.IsUsage() }) }
|
|
||||||
func (m MultiError) IsDecode() bool { return m.any(func(e Error) bool { return e.IsDecode() }) }
|
|
||||||
func (m MultiError) IsInternal() bool { return m.any(func(e Error) bool { return e.IsInternal() }) }
|
|
||||||
|
|
||||||
// Cause returns nil for MultiError; there is no unique underlying cause in the
|
|
||||||
// general case.
|
|
||||||
//
|
|
||||||
// Note: we could conceivably return a non-nil Cause only when there is exactly
|
|
||||||
// one child error with a Cause. However, it would be brittle for client code
|
|
||||||
// to rely on the arity of causes inside a MultiError, so we have opted not to
|
|
||||||
// provide this functionality. Clients which really wish to access the Causes
|
|
||||||
// of the underlying errors are free to iterate through the errors themselves.
|
|
||||||
func (m MultiError) Cause() error { return nil }
|
|
||||||
|
|
||||||
func (m MultiError) Error() string {
|
|
||||||
s, n := "", 0
|
|
||||||
for _, e := range m {
|
|
||||||
if e != nil {
|
|
||||||
if n == 0 {
|
|
||||||
s = e.Error()
|
|
||||||
}
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch n {
|
|
||||||
case 0:
|
|
||||||
return "(0 errors)"
|
|
||||||
case 1:
|
|
||||||
return s
|
|
||||||
case 2:
|
|
||||||
return s + " (and 1 other error)"
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s (and %d other errors)", s, n-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// any returns true if any element of m is an Error for which pred returns true.
|
|
||||||
func (m MultiError) any(pred func(Error) bool) bool {
|
|
||||||
for _, e := range m {
|
|
||||||
if ourErr, ok := e.(Error); ok && pred(ourErr) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
27
vendor/github.com/gorilla/sessions/LICENSE
generated
vendored
27
vendor/github.com/gorilla/sessions/LICENSE
generated
vendored
|
@ -1,27 +0,0 @@
|
||||||
Copyright (c) 2012 Rodrigo Moraes. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
90
vendor/github.com/gorilla/sessions/README.md
generated
vendored
90
vendor/github.com/gorilla/sessions/README.md
generated
vendored
|
@ -1,90 +0,0 @@
|
||||||
sessions
|
|
||||||
========
|
|
||||||
[![GoDoc](https://godoc.org/github.com/gorilla/sessions?status.svg)](https://godoc.org/github.com/gorilla/sessions) [![Build Status](https://travis-ci.org/gorilla/sessions.png?branch=master)](https://travis-ci.org/gorilla/sessions)
|
|
||||||
[![Sourcegraph](https://sourcegraph.com/github.com/gorilla/sessions/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/sessions?badge)
|
|
||||||
|
|
||||||
|
|
||||||
gorilla/sessions provides cookie and filesystem sessions and infrastructure for
|
|
||||||
custom session backends.
|
|
||||||
|
|
||||||
The key features are:
|
|
||||||
|
|
||||||
* Simple API: use it as an easy way to set signed (and optionally
|
|
||||||
encrypted) cookies.
|
|
||||||
* Built-in backends to store sessions in cookies or the filesystem.
|
|
||||||
* Flash messages: session values that last until read.
|
|
||||||
* Convenient way to switch session persistency (aka "remember me") and set
|
|
||||||
other attributes.
|
|
||||||
* Mechanism to rotate authentication and encryption keys.
|
|
||||||
* Multiple sessions per request, even using different backends.
|
|
||||||
* Interfaces and infrastructure for custom session backends: sessions from
|
|
||||||
different stores can be retrieved and batch-saved using a common API.
|
|
||||||
|
|
||||||
Let's start with an example that shows the sessions API in a nutshell:
|
|
||||||
|
|
||||||
```go
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"github.com/gorilla/sessions"
|
|
||||||
)
|
|
||||||
|
|
||||||
var store = sessions.NewCookieStore([]byte("something-very-secret"))
|
|
||||||
|
|
||||||
func MyHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// Get a session. We're ignoring the error resulted from decoding an
|
|
||||||
// existing session: Get() always returns a session, even if empty.
|
|
||||||
session, _ := store.Get(r, "session-name")
|
|
||||||
// Set some session values.
|
|
||||||
session.Values["foo"] = "bar"
|
|
||||||
session.Values[42] = 43
|
|
||||||
// Save it before we write to the response/return from the handler.
|
|
||||||
session.Save(r, w)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
First we initialize a session store calling `NewCookieStore()` and passing a
|
|
||||||
secret key used to authenticate the session. Inside the handler, we call
|
|
||||||
`store.Get()` to retrieve an existing session or create a new one. Then we set
|
|
||||||
some session values in session.Values, which is a `map[interface{}]interface{}`.
|
|
||||||
And finally we call `session.Save()` to save the session in the response.
|
|
||||||
|
|
||||||
Important Note: If you aren't using gorilla/mux, you need to wrap your handlers
|
|
||||||
with
|
|
||||||
[`context.ClearHandler`](http://www.gorillatoolkit.org/pkg/context#ClearHandler)
|
|
||||||
or else you will leak memory! An easy way to do this is to wrap the top-level
|
|
||||||
mux when calling http.ListenAndServe:
|
|
||||||
|
|
||||||
```go
|
|
||||||
http.ListenAndServe(":8080", context.ClearHandler(http.DefaultServeMux))
|
|
||||||
```
|
|
||||||
|
|
||||||
The ClearHandler function is provided by the gorilla/context package.
|
|
||||||
|
|
||||||
More examples are available [on the Gorilla
|
|
||||||
website](http://www.gorillatoolkit.org/pkg/sessions).
|
|
||||||
|
|
||||||
## Store Implementations
|
|
||||||
|
|
||||||
Other implementations of the `sessions.Store` interface:
|
|
||||||
|
|
||||||
* [github.com/starJammer/gorilla-sessions-arangodb](https://github.com/starJammer/gorilla-sessions-arangodb) - ArangoDB
|
|
||||||
* [github.com/yosssi/boltstore](https://github.com/yosssi/boltstore) - Bolt
|
|
||||||
* [github.com/srinathgs/couchbasestore](https://github.com/srinathgs/couchbasestore) - Couchbase
|
|
||||||
* [github.com/denizeren/dynamostore](https://github.com/denizeren/dynamostore) - Dynamodb on AWS
|
|
||||||
* [github.com/savaki/dynastore](https://github.com/savaki/dynastore) - DynamoDB on AWS (Official AWS library)
|
|
||||||
* [github.com/bradleypeabody/gorilla-sessions-memcache](https://github.com/bradleypeabody/gorilla-sessions-memcache) - Memcache
|
|
||||||
* [github.com/dsoprea/go-appengine-sessioncascade](https://github.com/dsoprea/go-appengine-sessioncascade) - Memcache/Datastore/Context in AppEngine
|
|
||||||
* [github.com/kidstuff/mongostore](https://github.com/kidstuff/mongostore) - MongoDB
|
|
||||||
* [github.com/srinathgs/mysqlstore](https://github.com/srinathgs/mysqlstore) - MySQL
|
|
||||||
* [github.com/EnumApps/clustersqlstore](https://github.com/EnumApps/clustersqlstore) - MySQL Cluster
|
|
||||||
* [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL
|
|
||||||
* [github.com/boj/redistore](https://github.com/boj/redistore) - Redis
|
|
||||||
* [github.com/boj/rethinkstore](https://github.com/boj/rethinkstore) - RethinkDB
|
|
||||||
* [github.com/boj/riakstore](https://github.com/boj/riakstore) - Riak
|
|
||||||
* [github.com/michaeljs1990/sqlitestore](https://github.com/michaeljs1990/sqlitestore) - SQLite
|
|
||||||
* [github.com/wader/gormstore](https://github.com/wader/gormstore) - GORM (MySQL, PostgreSQL, SQLite)
|
|
||||||
* [github.com/gernest/qlstore](https://github.com/gernest/qlstore) - ql
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
BSD licensed. See the LICENSE file for details.
|
|
198
vendor/github.com/gorilla/sessions/doc.go
generated
vendored
198
vendor/github.com/gorilla/sessions/doc.go
generated
vendored
|
@ -1,198 +0,0 @@
|
||||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package sessions provides cookie and filesystem sessions and
|
|
||||||
infrastructure for custom session backends.
|
|
||||||
|
|
||||||
The key features are:
|
|
||||||
|
|
||||||
* Simple API: use it as an easy way to set signed (and optionally
|
|
||||||
encrypted) cookies.
|
|
||||||
* Built-in backends to store sessions in cookies or the filesystem.
|
|
||||||
* Flash messages: session values that last until read.
|
|
||||||
* Convenient way to switch session persistency (aka "remember me") and set
|
|
||||||
other attributes.
|
|
||||||
* Mechanism to rotate authentication and encryption keys.
|
|
||||||
* Multiple sessions per request, even using different backends.
|
|
||||||
* Interfaces and infrastructure for custom session backends: sessions from
|
|
||||||
different stores can be retrieved and batch-saved using a common API.
|
|
||||||
|
|
||||||
Let's start with an example that shows the sessions API in a nutshell:
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"github.com/gorilla/sessions"
|
|
||||||
)
|
|
||||||
|
|
||||||
var store = sessions.NewCookieStore([]byte("something-very-secret"))
|
|
||||||
|
|
||||||
func MyHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// Get a session. Get() always returns a session, even if empty.
|
|
||||||
session, err := store.Get(r, "session-name")
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set some session values.
|
|
||||||
session.Values["foo"] = "bar"
|
|
||||||
session.Values[42] = 43
|
|
||||||
// Save it before we write to the response/return from the handler.
|
|
||||||
session.Save(r, w)
|
|
||||||
}
|
|
||||||
|
|
||||||
First we initialize a session store calling NewCookieStore() and passing a
|
|
||||||
secret key used to authenticate the session. Inside the handler, we call
|
|
||||||
store.Get() to retrieve an existing session or a new one. Then we set some
|
|
||||||
session values in session.Values, which is a map[interface{}]interface{}.
|
|
||||||
And finally we call session.Save() to save the session in the response.
|
|
||||||
|
|
||||||
Note that in production code, we should check for errors when calling
|
|
||||||
session.Save(r, w), and either display an error message or otherwise handle it.
|
|
||||||
|
|
||||||
Save must be called before writing to the response, otherwise the session
|
|
||||||
cookie will not be sent to the client.
|
|
||||||
|
|
||||||
Important Note: If you aren't using gorilla/mux, you need to wrap your handlers
|
|
||||||
with context.ClearHandler as or else you will leak memory! An easy way to do this
|
|
||||||
is to wrap the top-level mux when calling http.ListenAndServe:
|
|
||||||
|
|
||||||
http.ListenAndServe(":8080", context.ClearHandler(http.DefaultServeMux))
|
|
||||||
|
|
||||||
The ClearHandler function is provided by the gorilla/context package.
|
|
||||||
|
|
||||||
That's all you need to know for the basic usage. Let's take a look at other
|
|
||||||
options, starting with flash messages.
|
|
||||||
|
|
||||||
Flash messages are session values that last until read. The term appeared with
|
|
||||||
Ruby On Rails a few years back. When we request a flash message, it is removed
|
|
||||||
from the session. To add a flash, call session.AddFlash(), and to get all
|
|
||||||
flashes, call session.Flashes(). Here is an example:
|
|
||||||
|
|
||||||
func MyHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// Get a session.
|
|
||||||
session, err := store.Get(r, "session-name")
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the previously flashes, if any.
|
|
||||||
if flashes := session.Flashes(); len(flashes) > 0 {
|
|
||||||
// Use the flash values.
|
|
||||||
} else {
|
|
||||||
// Set a new flash.
|
|
||||||
session.AddFlash("Hello, flash messages world!")
|
|
||||||
}
|
|
||||||
session.Save(r, w)
|
|
||||||
}
|
|
||||||
|
|
||||||
Flash messages are useful to set information to be read after a redirection,
|
|
||||||
like after form submissions.
|
|
||||||
|
|
||||||
There may also be cases where you want to store a complex datatype within a
|
|
||||||
session, such as a struct. Sessions are serialised using the encoding/gob package,
|
|
||||||
so it is easy to register new datatypes for storage in sessions:
|
|
||||||
|
|
||||||
import(
|
|
||||||
"encoding/gob"
|
|
||||||
"github.com/gorilla/sessions"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Person struct {
|
|
||||||
FirstName string
|
|
||||||
LastName string
|
|
||||||
Email string
|
|
||||||
Age int
|
|
||||||
}
|
|
||||||
|
|
||||||
type M map[string]interface{}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
|
|
||||||
gob.Register(&Person{})
|
|
||||||
gob.Register(&M{})
|
|
||||||
}
|
|
||||||
|
|
||||||
As it's not possible to pass a raw type as a parameter to a function, gob.Register()
|
|
||||||
relies on us passing it a value of the desired type. In the example above we've passed
|
|
||||||
it a pointer to a struct and a pointer to a custom type representing a
|
|
||||||
map[string]interface. (We could have passed non-pointer values if we wished.) This will
|
|
||||||
then allow us to serialise/deserialise values of those types to and from our sessions.
|
|
||||||
|
|
||||||
Note that because session values are stored in a map[string]interface{}, there's
|
|
||||||
a need to type-assert data when retrieving it. We'll use the Person struct we registered above:
|
|
||||||
|
|
||||||
func MyHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
session, err := store.Get(r, "session-name")
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve our struct and type-assert it
|
|
||||||
val := session.Values["person"]
|
|
||||||
var person = &Person{}
|
|
||||||
if person, ok := val.(*Person); !ok {
|
|
||||||
// Handle the case that it's not an expected type
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we can use our person object
|
|
||||||
}
|
|
||||||
|
|
||||||
By default, session cookies last for a month. This is probably too long for
|
|
||||||
some cases, but it is easy to change this and other attributes during
|
|
||||||
runtime. Sessions can be configured individually or the store can be
|
|
||||||
configured and then all sessions saved using it will use that configuration.
|
|
||||||
We access session.Options or store.Options to set a new configuration. The
|
|
||||||
fields are basically a subset of http.Cookie fields. Let's change the
|
|
||||||
maximum age of a session to one week:
|
|
||||||
|
|
||||||
session.Options = &sessions.Options{
|
|
||||||
Path: "/",
|
|
||||||
MaxAge: 86400 * 7,
|
|
||||||
HttpOnly: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
Sometimes we may want to change authentication and/or encryption keys without
|
|
||||||
breaking existing sessions. The CookieStore supports key rotation, and to use
|
|
||||||
it you just need to set multiple authentication and encryption keys, in pairs,
|
|
||||||
to be tested in order:
|
|
||||||
|
|
||||||
var store = sessions.NewCookieStore(
|
|
||||||
[]byte("new-authentication-key"),
|
|
||||||
[]byte("new-encryption-key"),
|
|
||||||
[]byte("old-authentication-key"),
|
|
||||||
[]byte("old-encryption-key"),
|
|
||||||
)
|
|
||||||
|
|
||||||
New sessions will be saved using the first pair. Old sessions can still be
|
|
||||||
read because the first pair will fail, and the second will be tested. This
|
|
||||||
makes it easy to "rotate" secret keys and still be able to validate existing
|
|
||||||
sessions. Note: for all pairs the encryption key is optional; set it to nil
|
|
||||||
or omit it and and encryption won't be used.
|
|
||||||
|
|
||||||
Multiple sessions can be used in the same request, even with different
|
|
||||||
session backends. When this happens, calling Save() on each session
|
|
||||||
individually would be cumbersome, so we have a way to save all sessions
|
|
||||||
at once: it's sessions.Save(). Here's an example:
|
|
||||||
|
|
||||||
var store = sessions.NewCookieStore([]byte("something-very-secret"))
|
|
||||||
|
|
||||||
func MyHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// Get a session and set a value.
|
|
||||||
session1, _ := store.Get(r, "session-one")
|
|
||||||
session1.Values["foo"] = "bar"
|
|
||||||
// Get another session and set another value.
|
|
||||||
session2, _ := store.Get(r, "session-two")
|
|
||||||
session2.Values[42] = 43
|
|
||||||
// Save all sessions.
|
|
||||||
sessions.Save(r, w)
|
|
||||||
}
|
|
||||||
|
|
||||||
This is possible because when we call Get() from a session store, it adds the
|
|
||||||
session to a common registry. Save() uses it to save all registered sessions.
|
|
||||||
*/
|
|
||||||
package sessions
|
|
102
vendor/github.com/gorilla/sessions/lex.go
generated
vendored
102
vendor/github.com/gorilla/sessions/lex.go
generated
vendored
|
@ -1,102 +0,0 @@
|
||||||
// This file contains code adapted from the Go standard library
|
|
||||||
// https://github.com/golang/go/blob/39ad0fd0789872f9469167be7fe9578625ff246e/src/net/http/lex.go
|
|
||||||
|
|
||||||
package sessions
|
|
||||||
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
var isTokenTable = [127]bool{
|
|
||||||
'!': true,
|
|
||||||
'#': true,
|
|
||||||
'$': true,
|
|
||||||
'%': true,
|
|
||||||
'&': true,
|
|
||||||
'\'': true,
|
|
||||||
'*': true,
|
|
||||||
'+': true,
|
|
||||||
'-': true,
|
|
||||||
'.': true,
|
|
||||||
'0': true,
|
|
||||||
'1': true,
|
|
||||||
'2': true,
|
|
||||||
'3': true,
|
|
||||||
'4': true,
|
|
||||||
'5': true,
|
|
||||||
'6': true,
|
|
||||||
'7': true,
|
|
||||||
'8': true,
|
|
||||||
'9': true,
|
|
||||||
'A': true,
|
|
||||||
'B': true,
|
|
||||||
'C': true,
|
|
||||||
'D': true,
|
|
||||||
'E': true,
|
|
||||||
'F': true,
|
|
||||||
'G': true,
|
|
||||||
'H': true,
|
|
||||||
'I': true,
|
|
||||||
'J': true,
|
|
||||||
'K': true,
|
|
||||||
'L': true,
|
|
||||||
'M': true,
|
|
||||||
'N': true,
|
|
||||||
'O': true,
|
|
||||||
'P': true,
|
|
||||||
'Q': true,
|
|
||||||
'R': true,
|
|
||||||
'S': true,
|
|
||||||
'T': true,
|
|
||||||
'U': true,
|
|
||||||
'W': true,
|
|
||||||
'V': true,
|
|
||||||
'X': true,
|
|
||||||
'Y': true,
|
|
||||||
'Z': true,
|
|
||||||
'^': true,
|
|
||||||
'_': true,
|
|
||||||
'`': true,
|
|
||||||
'a': true,
|
|
||||||
'b': true,
|
|
||||||
'c': true,
|
|
||||||
'd': true,
|
|
||||||
'e': true,
|
|
||||||
'f': true,
|
|
||||||
'g': true,
|
|
||||||
'h': true,
|
|
||||||
'i': true,
|
|
||||||
'j': true,
|
|
||||||
'k': true,
|
|
||||||
'l': true,
|
|
||||||
'm': true,
|
|
||||||
'n': true,
|
|
||||||
'o': true,
|
|
||||||
'p': true,
|
|
||||||
'q': true,
|
|
||||||
'r': true,
|
|
||||||
's': true,
|
|
||||||
't': true,
|
|
||||||
'u': true,
|
|
||||||
'v': true,
|
|
||||||
'w': true,
|
|
||||||
'x': true,
|
|
||||||
'y': true,
|
|
||||||
'z': true,
|
|
||||||
'|': true,
|
|
||||||
'~': true,
|
|
||||||
}
|
|
||||||
|
|
||||||
func isToken(r rune) bool {
|
|
||||||
i := int(r)
|
|
||||||
return i < len(isTokenTable) && isTokenTable[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
func isNotToken(r rune) bool {
|
|
||||||
return !isToken(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isCookieNameValid(raw string) bool {
|
|
||||||
if raw == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return strings.IndexFunc(raw, isNotToken) < 0
|
|
||||||
}
|
|
241
vendor/github.com/gorilla/sessions/sessions.go
generated
vendored
241
vendor/github.com/gorilla/sessions/sessions.go
generated
vendored
|
@ -1,241 +0,0 @@
|
||||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package sessions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/gob"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gorilla/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Default flashes key.
|
|
||||||
const flashesKey = "_flash"
|
|
||||||
|
|
||||||
// Options --------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Options stores configuration for a session or session store.
|
|
||||||
//
|
|
||||||
// Fields are a subset of http.Cookie fields.
|
|
||||||
type Options struct {
|
|
||||||
Path string
|
|
||||||
Domain string
|
|
||||||
// MaxAge=0 means no 'Max-Age' attribute specified.
|
|
||||||
// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'.
|
|
||||||
// MaxAge>0 means Max-Age attribute present and given in seconds.
|
|
||||||
MaxAge int
|
|
||||||
Secure bool
|
|
||||||
HttpOnly bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Session --------------------------------------------------------------------
|
|
||||||
|
|
||||||
// NewSession is called by session stores to create a new session instance.
|
|
||||||
func NewSession(store Store, name string) *Session {
|
|
||||||
return &Session{
|
|
||||||
Values: make(map[interface{}]interface{}),
|
|
||||||
store: store,
|
|
||||||
name: name,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Session stores the values and optional configuration for a session.
|
|
||||||
type Session struct {
|
|
||||||
// The ID of the session, generated by stores. It should not be used for
|
|
||||||
// user data.
|
|
||||||
ID string
|
|
||||||
// Values contains the user-data for the session.
|
|
||||||
Values map[interface{}]interface{}
|
|
||||||
Options *Options
|
|
||||||
IsNew bool
|
|
||||||
store Store
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flashes returns a slice of flash messages from the session.
|
|
||||||
//
|
|
||||||
// A single variadic argument is accepted, and it is optional: it defines
|
|
||||||
// the flash key. If not defined "_flash" is used by default.
|
|
||||||
func (s *Session) Flashes(vars ...string) []interface{} {
|
|
||||||
var flashes []interface{}
|
|
||||||
key := flashesKey
|
|
||||||
if len(vars) > 0 {
|
|
||||||
key = vars[0]
|
|
||||||
}
|
|
||||||
if v, ok := s.Values[key]; ok {
|
|
||||||
// Drop the flashes and return it.
|
|
||||||
delete(s.Values, key)
|
|
||||||
flashes = v.([]interface{})
|
|
||||||
}
|
|
||||||
return flashes
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddFlash adds a flash message to the session.
|
|
||||||
//
|
|
||||||
// A single variadic argument is accepted, and it is optional: it defines
|
|
||||||
// the flash key. If not defined "_flash" is used by default.
|
|
||||||
func (s *Session) AddFlash(value interface{}, vars ...string) {
|
|
||||||
key := flashesKey
|
|
||||||
if len(vars) > 0 {
|
|
||||||
key = vars[0]
|
|
||||||
}
|
|
||||||
var flashes []interface{}
|
|
||||||
if v, ok := s.Values[key]; ok {
|
|
||||||
flashes = v.([]interface{})
|
|
||||||
}
|
|
||||||
s.Values[key] = append(flashes, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save is a convenience method to save this session. It is the same as calling
|
|
||||||
// store.Save(request, response, session). You should call Save before writing to
|
|
||||||
// the response or returning from the handler.
|
|
||||||
func (s *Session) Save(r *http.Request, w http.ResponseWriter) error {
|
|
||||||
return s.store.Save(r, w, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns the name used to register the session.
|
|
||||||
func (s *Session) Name() string {
|
|
||||||
return s.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store returns the session store used to register the session.
|
|
||||||
func (s *Session) Store() Store {
|
|
||||||
return s.store
|
|
||||||
}
|
|
||||||
|
|
||||||
// Registry -------------------------------------------------------------------
|
|
||||||
|
|
||||||
// sessionInfo stores a session tracked by the registry.
|
|
||||||
type sessionInfo struct {
|
|
||||||
s *Session
|
|
||||||
e error
|
|
||||||
}
|
|
||||||
|
|
||||||
// contextKey is the type used to store the registry in the context.
|
|
||||||
type contextKey int
|
|
||||||
|
|
||||||
// registryKey is the key used to store the registry in the context.
|
|
||||||
const registryKey contextKey = 0
|
|
||||||
|
|
||||||
// GetRegistry returns a registry instance for the current request.
|
|
||||||
func GetRegistry(r *http.Request) *Registry {
|
|
||||||
registry := context.Get(r, registryKey)
|
|
||||||
if registry != nil {
|
|
||||||
return registry.(*Registry)
|
|
||||||
}
|
|
||||||
newRegistry := &Registry{
|
|
||||||
request: r,
|
|
||||||
sessions: make(map[string]sessionInfo),
|
|
||||||
}
|
|
||||||
context.Set(r, registryKey, newRegistry)
|
|
||||||
return newRegistry
|
|
||||||
}
|
|
||||||
|
|
||||||
// Registry stores sessions used during a request.
|
|
||||||
type Registry struct {
|
|
||||||
request *http.Request
|
|
||||||
sessions map[string]sessionInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get registers and returns a session for the given name and session store.
|
|
||||||
//
|
|
||||||
// It returns a new session if there are no sessions registered for the name.
|
|
||||||
func (s *Registry) Get(store Store, name string) (session *Session, err error) {
|
|
||||||
if !isCookieNameValid(name) {
|
|
||||||
return nil, fmt.Errorf("sessions: invalid character in cookie name: %s", name)
|
|
||||||
}
|
|
||||||
if info, ok := s.sessions[name]; ok {
|
|
||||||
session, err = info.s, info.e
|
|
||||||
} else {
|
|
||||||
session, err = store.New(s.request, name)
|
|
||||||
session.name = name
|
|
||||||
s.sessions[name] = sessionInfo{s: session, e: err}
|
|
||||||
}
|
|
||||||
session.store = store
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save saves all sessions registered for the current request.
|
|
||||||
func (s *Registry) Save(w http.ResponseWriter) error {
|
|
||||||
var errMulti MultiError
|
|
||||||
for name, info := range s.sessions {
|
|
||||||
session := info.s
|
|
||||||
if session.store == nil {
|
|
||||||
errMulti = append(errMulti, fmt.Errorf(
|
|
||||||
"sessions: missing store for session %q", name))
|
|
||||||
} else if err := session.store.Save(s.request, w, session); err != nil {
|
|
||||||
errMulti = append(errMulti, fmt.Errorf(
|
|
||||||
"sessions: error saving session %q -- %v", name, err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if errMulti != nil {
|
|
||||||
return errMulti
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helpers --------------------------------------------------------------------
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
gob.Register([]interface{}{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save saves all sessions used during the current request.
|
|
||||||
func Save(r *http.Request, w http.ResponseWriter) error {
|
|
||||||
return GetRegistry(r).Save(w)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCookie returns an http.Cookie with the options set. It also sets
|
|
||||||
// the Expires field calculated based on the MaxAge value, for Internet
|
|
||||||
// Explorer compatibility.
|
|
||||||
func NewCookie(name, value string, options *Options) *http.Cookie {
|
|
||||||
cookie := &http.Cookie{
|
|
||||||
Name: name,
|
|
||||||
Value: value,
|
|
||||||
Path: options.Path,
|
|
||||||
Domain: options.Domain,
|
|
||||||
MaxAge: options.MaxAge,
|
|
||||||
Secure: options.Secure,
|
|
||||||
HttpOnly: options.HttpOnly,
|
|
||||||
}
|
|
||||||
if options.MaxAge > 0 {
|
|
||||||
d := time.Duration(options.MaxAge) * time.Second
|
|
||||||
cookie.Expires = time.Now().Add(d)
|
|
||||||
} else if options.MaxAge < 0 {
|
|
||||||
// Set it to the past to expire now.
|
|
||||||
cookie.Expires = time.Unix(1, 0)
|
|
||||||
}
|
|
||||||
return cookie
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
// MultiError stores multiple errors.
|
|
||||||
//
|
|
||||||
// Borrowed from the App Engine SDK.
|
|
||||||
type MultiError []error
|
|
||||||
|
|
||||||
func (m MultiError) Error() string {
|
|
||||||
s, n := "", 0
|
|
||||||
for _, e := range m {
|
|
||||||
if e != nil {
|
|
||||||
if n == 0 {
|
|
||||||
s = e.Error()
|
|
||||||
}
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch n {
|
|
||||||
case 0:
|
|
||||||
return "(0 errors)"
|
|
||||||
case 1:
|
|
||||||
return s
|
|
||||||
case 2:
|
|
||||||
return s + " (and 1 other error)"
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s (and %d other errors)", s, n-1)
|
|
||||||
}
|
|
295
vendor/github.com/gorilla/sessions/store.go
generated
vendored
295
vendor/github.com/gorilla/sessions/store.go
generated
vendored
|
@ -1,295 +0,0 @@
|
||||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package sessions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base32"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/gorilla/securecookie"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Store is an interface for custom session stores.
|
|
||||||
//
|
|
||||||
// See CookieStore and FilesystemStore for examples.
|
|
||||||
type Store interface {
|
|
||||||
// Get should return a cached session.
|
|
||||||
Get(r *http.Request, name string) (*Session, error)
|
|
||||||
|
|
||||||
// New should create and return a new session.
|
|
||||||
//
|
|
||||||
// Note that New should never return a nil session, even in the case of
|
|
||||||
// an error if using the Registry infrastructure to cache the session.
|
|
||||||
New(r *http.Request, name string) (*Session, error)
|
|
||||||
|
|
||||||
// Save should persist session to the underlying store implementation.
|
|
||||||
Save(r *http.Request, w http.ResponseWriter, s *Session) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// CookieStore ----------------------------------------------------------------
|
|
||||||
|
|
||||||
// NewCookieStore returns a new CookieStore.
|
|
||||||
//
|
|
||||||
// Keys are defined in pairs to allow key rotation, but the common case is
|
|
||||||
// to set a single authentication key and optionally an encryption key.
|
|
||||||
//
|
|
||||||
// The first key in a pair is used for authentication and the second for
|
|
||||||
// encryption. The encryption key can be set to nil or omitted in the last
|
|
||||||
// pair, but the authentication key is required in all pairs.
|
|
||||||
//
|
|
||||||
// It is recommended to use an authentication key with 32 or 64 bytes.
|
|
||||||
// The encryption key, if set, must be either 16, 24, or 32 bytes to select
|
|
||||||
// AES-128, AES-192, or AES-256 modes.
|
|
||||||
//
|
|
||||||
// Use the convenience function securecookie.GenerateRandomKey() to create
|
|
||||||
// strong keys.
|
|
||||||
func NewCookieStore(keyPairs ...[]byte) *CookieStore {
|
|
||||||
cs := &CookieStore{
|
|
||||||
Codecs: securecookie.CodecsFromPairs(keyPairs...),
|
|
||||||
Options: &Options{
|
|
||||||
Path: "/",
|
|
||||||
MaxAge: 86400 * 30,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
cs.MaxAge(cs.Options.MaxAge)
|
|
||||||
return cs
|
|
||||||
}
|
|
||||||
|
|
||||||
// CookieStore stores sessions using secure cookies.
|
|
||||||
type CookieStore struct {
|
|
||||||
Codecs []securecookie.Codec
|
|
||||||
Options *Options // default configuration
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns a session for the given name after adding it to the registry.
|
|
||||||
//
|
|
||||||
// It returns a new session if the sessions doesn't exist. Access IsNew on
|
|
||||||
// the session to check if it is an existing session or a new one.
|
|
||||||
//
|
|
||||||
// It returns a new session and an error if the session exists but could
|
|
||||||
// not be decoded.
|
|
||||||
func (s *CookieStore) Get(r *http.Request, name string) (*Session, error) {
|
|
||||||
return GetRegistry(r).Get(s, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// New returns a session for the given name without adding it to the registry.
|
|
||||||
//
|
|
||||||
// The difference between New() and Get() is that calling New() twice will
|
|
||||||
// decode the session data twice, while Get() registers and reuses the same
|
|
||||||
// decoded session after the first call.
|
|
||||||
func (s *CookieStore) New(r *http.Request, name string) (*Session, error) {
|
|
||||||
session := NewSession(s, name)
|
|
||||||
opts := *s.Options
|
|
||||||
session.Options = &opts
|
|
||||||
session.IsNew = true
|
|
||||||
var err error
|
|
||||||
if c, errCookie := r.Cookie(name); errCookie == nil {
|
|
||||||
err = securecookie.DecodeMulti(name, c.Value, &session.Values,
|
|
||||||
s.Codecs...)
|
|
||||||
if err == nil {
|
|
||||||
session.IsNew = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return session, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save adds a single session to the response.
|
|
||||||
func (s *CookieStore) Save(r *http.Request, w http.ResponseWriter,
|
|
||||||
session *Session) error {
|
|
||||||
encoded, err := securecookie.EncodeMulti(session.Name(), session.Values,
|
|
||||||
s.Codecs...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
http.SetCookie(w, NewCookie(session.Name(), encoded, session.Options))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MaxAge sets the maximum age for the store and the underlying cookie
|
|
||||||
// implementation. Individual sessions can be deleted by setting Options.MaxAge
|
|
||||||
// = -1 for that session.
|
|
||||||
func (s *CookieStore) MaxAge(age int) {
|
|
||||||
s.Options.MaxAge = age
|
|
||||||
|
|
||||||
// Set the maxAge for each securecookie instance.
|
|
||||||
for _, codec := range s.Codecs {
|
|
||||||
if sc, ok := codec.(*securecookie.SecureCookie); ok {
|
|
||||||
sc.MaxAge(age)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FilesystemStore ------------------------------------------------------------
|
|
||||||
|
|
||||||
var fileMutex sync.RWMutex
|
|
||||||
|
|
||||||
// NewFilesystemStore returns a new FilesystemStore.
|
|
||||||
//
|
|
||||||
// The path argument is the directory where sessions will be saved. If empty
|
|
||||||
// it will use os.TempDir().
|
|
||||||
//
|
|
||||||
// See NewCookieStore() for a description of the other parameters.
|
|
||||||
func NewFilesystemStore(path string, keyPairs ...[]byte) *FilesystemStore {
|
|
||||||
if path == "" {
|
|
||||||
path = os.TempDir()
|
|
||||||
}
|
|
||||||
fs := &FilesystemStore{
|
|
||||||
Codecs: securecookie.CodecsFromPairs(keyPairs...),
|
|
||||||
Options: &Options{
|
|
||||||
Path: "/",
|
|
||||||
MaxAge: 86400 * 30,
|
|
||||||
},
|
|
||||||
path: path,
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.MaxAge(fs.Options.MaxAge)
|
|
||||||
return fs
|
|
||||||
}
|
|
||||||
|
|
||||||
// FilesystemStore stores sessions in the filesystem.
|
|
||||||
//
|
|
||||||
// It also serves as a reference for custom stores.
|
|
||||||
//
|
|
||||||
// This store is still experimental and not well tested. Feedback is welcome.
|
|
||||||
type FilesystemStore struct {
|
|
||||||
Codecs []securecookie.Codec
|
|
||||||
Options *Options // default configuration
|
|
||||||
path string
|
|
||||||
}
|
|
||||||
|
|
||||||
// MaxLength restricts the maximum length of new sessions to l.
|
|
||||||
// If l is 0 there is no limit to the size of a session, use with caution.
|
|
||||||
// The default for a new FilesystemStore is 4096.
|
|
||||||
func (s *FilesystemStore) MaxLength(l int) {
|
|
||||||
for _, c := range s.Codecs {
|
|
||||||
if codec, ok := c.(*securecookie.SecureCookie); ok {
|
|
||||||
codec.MaxLength(l)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns a session for the given name after adding it to the registry.
|
|
||||||
//
|
|
||||||
// See CookieStore.Get().
|
|
||||||
func (s *FilesystemStore) Get(r *http.Request, name string) (*Session, error) {
|
|
||||||
return GetRegistry(r).Get(s, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// New returns a session for the given name without adding it to the registry.
|
|
||||||
//
|
|
||||||
// See CookieStore.New().
|
|
||||||
func (s *FilesystemStore) New(r *http.Request, name string) (*Session, error) {
|
|
||||||
session := NewSession(s, name)
|
|
||||||
opts := *s.Options
|
|
||||||
session.Options = &opts
|
|
||||||
session.IsNew = true
|
|
||||||
var err error
|
|
||||||
if c, errCookie := r.Cookie(name); errCookie == nil {
|
|
||||||
err = securecookie.DecodeMulti(name, c.Value, &session.ID, s.Codecs...)
|
|
||||||
if err == nil {
|
|
||||||
err = s.load(session)
|
|
||||||
if err == nil {
|
|
||||||
session.IsNew = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return session, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save adds a single session to the response.
|
|
||||||
//
|
|
||||||
// If the Options.MaxAge of the session is <= 0 then the session file will be
|
|
||||||
// deleted from the store path. With this process it enforces the properly
|
|
||||||
// session cookie handling so no need to trust in the cookie management in the
|
|
||||||
// web browser.
|
|
||||||
func (s *FilesystemStore) Save(r *http.Request, w http.ResponseWriter,
|
|
||||||
session *Session) error {
|
|
||||||
// Delete if max-age is <= 0
|
|
||||||
if session.Options.MaxAge <= 0 {
|
|
||||||
if err := s.erase(session); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
http.SetCookie(w, NewCookie(session.Name(), "", session.Options))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if session.ID == "" {
|
|
||||||
// Because the ID is used in the filename, encode it to
|
|
||||||
// use alphanumeric characters only.
|
|
||||||
session.ID = strings.TrimRight(
|
|
||||||
base32.StdEncoding.EncodeToString(
|
|
||||||
securecookie.GenerateRandomKey(32)), "=")
|
|
||||||
}
|
|
||||||
if err := s.save(session); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
encoded, err := securecookie.EncodeMulti(session.Name(), session.ID,
|
|
||||||
s.Codecs...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
http.SetCookie(w, NewCookie(session.Name(), encoded, session.Options))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MaxAge sets the maximum age for the store and the underlying cookie
|
|
||||||
// implementation. Individual sessions can be deleted by setting Options.MaxAge
|
|
||||||
// = -1 for that session.
|
|
||||||
func (s *FilesystemStore) MaxAge(age int) {
|
|
||||||
s.Options.MaxAge = age
|
|
||||||
|
|
||||||
// Set the maxAge for each securecookie instance.
|
|
||||||
for _, codec := range s.Codecs {
|
|
||||||
if sc, ok := codec.(*securecookie.SecureCookie); ok {
|
|
||||||
sc.MaxAge(age)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// save writes encoded session.Values to a file.
|
|
||||||
func (s *FilesystemStore) save(session *Session) error {
|
|
||||||
encoded, err := securecookie.EncodeMulti(session.Name(), session.Values,
|
|
||||||
s.Codecs...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
filename := filepath.Join(s.path, "session_"+session.ID)
|
|
||||||
fileMutex.Lock()
|
|
||||||
defer fileMutex.Unlock()
|
|
||||||
return ioutil.WriteFile(filename, []byte(encoded), 0600)
|
|
||||||
}
|
|
||||||
|
|
||||||
// load reads a file and decodes its content into session.Values.
|
|
||||||
func (s *FilesystemStore) load(session *Session) error {
|
|
||||||
filename := filepath.Join(s.path, "session_"+session.ID)
|
|
||||||
fileMutex.RLock()
|
|
||||||
defer fileMutex.RUnlock()
|
|
||||||
fdata, err := ioutil.ReadFile(filename)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = securecookie.DecodeMulti(session.Name(), string(fdata),
|
|
||||||
&session.Values, s.Codecs...); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete session file
|
|
||||||
func (s *FilesystemStore) erase(session *Session) error {
|
|
||||||
filename := filepath.Join(s.path, "session_"+session.ID)
|
|
||||||
|
|
||||||
fileMutex.RLock()
|
|
||||||
defer fileMutex.RUnlock()
|
|
||||||
|
|
||||||
err := os.Remove(filename)
|
|
||||||
return err
|
|
||||||
}
|
|
21
vendor/github.com/labstack/echo-contrib/LICENSE
generated
vendored
21
vendor/github.com/labstack/echo-contrib/LICENSE
generated
vendored
|
@ -1,21 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2017 LabStack
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
65
vendor/github.com/labstack/echo-contrib/session/session.go
generated
vendored
65
vendor/github.com/labstack/echo-contrib/session/session.go
generated
vendored
|
@ -1,65 +0,0 @@
|
||||||
package session
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/gorilla/sessions"
|
|
||||||
"github.com/labstack/echo"
|
|
||||||
"github.com/labstack/echo/middleware"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
// Config defines the config for CasbinAuth middleware.
|
|
||||||
Config struct {
|
|
||||||
// Skipper defines a function to skip middleware.
|
|
||||||
Skipper middleware.Skipper
|
|
||||||
|
|
||||||
// Session store.
|
|
||||||
// Required.
|
|
||||||
Store sessions.Store
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
key = "_session_store"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// DefaultConfig is the default Session middleware config.
|
|
||||||
DefaultConfig = Config{
|
|
||||||
Skipper: middleware.DefaultSkipper,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Get returns a named session.
|
|
||||||
func Get(name string, c echo.Context) (*sessions.Session, error) {
|
|
||||||
store := c.Get(key).(sessions.Store)
|
|
||||||
return store.Get(c.Request(), name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Middleware returns a Session middleware.
|
|
||||||
func Middleware(store sessions.Store) echo.MiddlewareFunc {
|
|
||||||
c := DefaultConfig
|
|
||||||
c.Store = store
|
|
||||||
return MiddlewareWithConfig(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MiddlewareWithConfig returns a Sessions middleware with config.
|
|
||||||
// See `Middleware()`.
|
|
||||||
func MiddlewareWithConfig(config Config) echo.MiddlewareFunc {
|
|
||||||
// Defaults
|
|
||||||
if config.Skipper == nil {
|
|
||||||
config.Skipper = DefaultConfig.Skipper
|
|
||||||
}
|
|
||||||
if config.Store == nil {
|
|
||||||
panic("echo: session middleware requires store")
|
|
||||||
}
|
|
||||||
|
|
||||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
|
||||||
return func(c echo.Context) error {
|
|
||||||
if config.Skipper(c) {
|
|
||||||
return next(c)
|
|
||||||
}
|
|
||||||
c.Set(key, config.Store)
|
|
||||||
return next(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
25
vendor/github.com/labstack/echo/.editorconfig
generated
vendored
Normal file
25
vendor/github.com/labstack/echo/.editorconfig
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# EditorConfig coding styles definitions. For more information about the
|
||||||
|
# properties used in this file, please see the EditorConfig documentation:
|
||||||
|
# http://editorconfig.org/
|
||||||
|
|
||||||
|
# indicate this is the root of the project
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
end_of_line = LF
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[Makefile]
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.go]
|
||||||
|
indent_style = tab
|
20
vendor/github.com/labstack/echo/.gitattributes
generated
vendored
Normal file
20
vendor/github.com/labstack/echo/.gitattributes
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# Automatically normalize line endings for all text-based files
|
||||||
|
# http://git-scm.com/docs/gitattributes#_end_of_line_conversion
|
||||||
|
* text=auto
|
||||||
|
|
||||||
|
# For the following file types, normalize line endings to LF on checking and
|
||||||
|
# prevent conversion to CRLF when they are checked out (this is required in
|
||||||
|
# order to prevent newline related issues)
|
||||||
|
.* text eol=lf
|
||||||
|
*.go text eol=lf
|
||||||
|
*.yml text eol=lf
|
||||||
|
*.html text eol=lf
|
||||||
|
*.css text eol=lf
|
||||||
|
*.js text eol=lf
|
||||||
|
*.json text eol=lf
|
||||||
|
LICENSE text eol=lf
|
||||||
|
|
||||||
|
# Exclude `website` and `cookbook` from GitHub's language statistics
|
||||||
|
# https://github.com/github/linguist#using-gitattributes
|
||||||
|
cookbook/* linguist-documentation
|
||||||
|
website/* linguist-documentation
|
8
vendor/github.com/labstack/echo/.gitignore
generated
vendored
Normal file
8
vendor/github.com/labstack/echo/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# Website
|
||||||
|
website/public
|
||||||
|
|
||||||
|
# Glide
|
||||||
|
vendor
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
_test
|
19
vendor/github.com/labstack/echo/.travis.yml
generated
vendored
Normal file
19
vendor/github.com/labstack/echo/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.7
|
||||||
|
- 1.8
|
||||||
|
- tip
|
||||||
|
install:
|
||||||
|
- go get golang.org/x/tools/cmd/cover
|
||||||
|
- go get github.com/Masterminds/glide
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
- go get github.com/modocache/gover
|
||||||
|
- glide install
|
||||||
|
script:
|
||||||
|
- go test -coverprofile=echo.coverprofile
|
||||||
|
- go test -coverprofile=middleware.coverprofile ./middleware
|
||||||
|
- gover
|
||||||
|
- goveralls -coverprofile=gover.coverprofile -service=travis-ci
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- go: tip
|
9
vendor/github.com/mattn/go-colorable/.travis.yml
generated
vendored
Normal file
9
vendor/github.com/mattn/go-colorable/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- tip
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
- go get golang.org/x/tools/cmd/cover
|
||||||
|
script:
|
||||||
|
- $HOME/gopath/bin/goveralls -repotoken xnXqRGwgW3SXIguzxf90ZSK1GPYZPaGrw
|
9
vendor/github.com/mattn/go-isatty/.travis.yml
generated
vendored
Normal file
9
vendor/github.com/mattn/go-isatty/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- tip
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
- go get golang.org/x/tools/cmd/cover
|
||||||
|
script:
|
||||||
|
- $HOME/gopath/bin/goveralls -repotoken 3gHdORO5k5ziZcWMBxnd9LrMZaJs8m9x5
|
4
vendor/github.com/mattn/go-sqlite3/.gitignore
generated
vendored
Normal file
4
vendor/github.com/mattn/go-sqlite3/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
*.db
|
||||||
|
*.exe
|
||||||
|
*.dll
|
||||||
|
*.o
|
19
vendor/github.com/mattn/go-sqlite3/.travis.yml
generated
vendored
Normal file
19
vendor/github.com/mattn/go-sqlite3/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
language: go
|
||||||
|
sudo: required
|
||||||
|
dist: trusty
|
||||||
|
env:
|
||||||
|
- GOTAGS=
|
||||||
|
- GOTAGS=libsqlite3
|
||||||
|
- GOTAGS=trace
|
||||||
|
- GOTAGS=vtable
|
||||||
|
go:
|
||||||
|
- 1.7.x
|
||||||
|
- 1.8.x
|
||||||
|
- 1.9.x
|
||||||
|
- master
|
||||||
|
before_install:
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
- go get golang.org/x/tools/cmd/cover
|
||||||
|
script:
|
||||||
|
- $HOME/gopath/bin/goveralls -repotoken 3qJVUE0iQwqnCbmNcDsjYu1nh4J4KIFXx
|
||||||
|
- go test -race -v . -tags "$GOTAGS"
|
15
vendor/github.com/valyala/bytebufferpool/.travis.yml
generated
vendored
Normal file
15
vendor/github.com/valyala/bytebufferpool/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.6
|
||||||
|
|
||||||
|
script:
|
||||||
|
# build test for supported platforms
|
||||||
|
- GOOS=linux go build
|
||||||
|
- GOOS=darwin go build
|
||||||
|
- GOOS=freebsd go build
|
||||||
|
- GOOS=windows go build
|
||||||
|
- GOARCH=386 go build
|
||||||
|
|
||||||
|
# run tests on a standard platform
|
||||||
|
- go test -v ./...
|
3
vendor/golang.org/x/crypto/AUTHORS
generated
vendored
Normal file
3
vendor/golang.org/x/crypto/AUTHORS
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# This source code refers to The Go Authors for copyright purposes.
|
||||||
|
# The master list of authors is in the main Go distribution,
|
||||||
|
# visible at https://tip.golang.org/AUTHORS.
|
3
vendor/golang.org/x/crypto/CONTRIBUTORS
generated
vendored
Normal file
3
vendor/golang.org/x/crypto/CONTRIBUTORS
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# This source code was written by the Go contributors.
|
||||||
|
# The master list of contributors is in the main Go distribution,
|
||||||
|
# visible at https://tip.golang.org/CONTRIBUTORS.
|
477
vendor/golang.org/x/crypto/acme/acme.go
generated
vendored
477
vendor/golang.org/x/crypto/acme/acme.go
generated
vendored
|
@ -14,7 +14,6 @@
|
||||||
package acme
|
package acme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
@ -23,6 +22,8 @@ import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/asn1"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -33,14 +34,26 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LetsEncryptURL is the Directory endpoint of Let's Encrypt CA.
|
const (
|
||||||
const LetsEncryptURL = "https://acme-v01.api.letsencrypt.org/directory"
|
// LetsEncryptURL is the Directory endpoint of Let's Encrypt CA.
|
||||||
|
LetsEncryptURL = "https://acme-v01.api.letsencrypt.org/directory"
|
||||||
|
|
||||||
|
// ALPNProto is the ALPN protocol name used by a CA server when validating
|
||||||
|
// tls-alpn-01 challenges.
|
||||||
|
//
|
||||||
|
// Package users must ensure their servers can negotiate the ACME ALPN in
|
||||||
|
// order for tls-alpn-01 challenge verifications to succeed.
|
||||||
|
// See the crypto/tls package's Config.NextProtos field.
|
||||||
|
ALPNProto = "acme-tls/1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// idPeACMEIdentifierV1 is the OID for the ACME extension for the TLS-ALPN challenge.
|
||||||
|
var idPeACMEIdentifierV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 30, 1}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
maxChainLen = 5 // max depth and breadth of a certificate chain
|
maxChainLen = 5 // max depth and breadth of a certificate chain
|
||||||
|
@ -64,6 +77,10 @@ const (
|
||||||
type Client struct {
|
type Client struct {
|
||||||
// Key is the account key used to register with a CA and sign requests.
|
// Key is the account key used to register with a CA and sign requests.
|
||||||
// Key.Public() must return a *rsa.PublicKey or *ecdsa.PublicKey.
|
// Key.Public() must return a *rsa.PublicKey or *ecdsa.PublicKey.
|
||||||
|
//
|
||||||
|
// The following algorithms are supported:
|
||||||
|
// RS256, ES256, ES384 and ES512.
|
||||||
|
// See RFC7518 for more details about the algorithms.
|
||||||
Key crypto.Signer
|
Key crypto.Signer
|
||||||
|
|
||||||
// HTTPClient optionally specifies an HTTP client to use
|
// HTTPClient optionally specifies an HTTP client to use
|
||||||
|
@ -76,6 +93,22 @@ type Client struct {
|
||||||
// will have no effect.
|
// will have no effect.
|
||||||
DirectoryURL string
|
DirectoryURL string
|
||||||
|
|
||||||
|
// RetryBackoff computes the duration after which the nth retry of a failed request
|
||||||
|
// should occur. The value of n for the first call on failure is 1.
|
||||||
|
// The values of r and resp are the request and response of the last failed attempt.
|
||||||
|
// If the returned value is negative or zero, no more retries are done and an error
|
||||||
|
// is returned to the caller of the original method.
|
||||||
|
//
|
||||||
|
// Requests which result in a 4xx client error are not retried,
|
||||||
|
// except for 400 Bad Request due to "bad nonce" errors and 429 Too Many Requests.
|
||||||
|
//
|
||||||
|
// If RetryBackoff is nil, a truncated exponential backoff algorithm
|
||||||
|
// with the ceiling of 10 seconds is used, where each subsequent retry n
|
||||||
|
// is done after either ("Retry-After" + jitter) or (2^n seconds + jitter),
|
||||||
|
// preferring the former if "Retry-After" header is found in the resp.
|
||||||
|
// The jitter is a random value up to 1 second.
|
||||||
|
RetryBackoff func(n int, r *http.Request, resp *http.Response) time.Duration
|
||||||
|
|
||||||
dirMu sync.Mutex // guards writes to dir
|
dirMu sync.Mutex // guards writes to dir
|
||||||
dir *Directory // cached result of Client's Discover method
|
dir *Directory // cached result of Client's Discover method
|
||||||
|
|
||||||
|
@ -95,19 +128,12 @@ func (c *Client) Discover(ctx context.Context) (Directory, error) {
|
||||||
return *c.dir, nil
|
return *c.dir, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
dirURL := c.DirectoryURL
|
res, err := c.get(ctx, c.directoryURL(), wantStatus(http.StatusOK))
|
||||||
if dirURL == "" {
|
|
||||||
dirURL = LetsEncryptURL
|
|
||||||
}
|
|
||||||
res, err := c.get(ctx, dirURL)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Directory{}, err
|
return Directory{}, err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
c.addNonce(res.Header)
|
c.addNonce(res.Header)
|
||||||
if res.StatusCode != http.StatusOK {
|
|
||||||
return Directory{}, responseError(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
var v struct {
|
var v struct {
|
||||||
Reg string `json:"new-reg"`
|
Reg string `json:"new-reg"`
|
||||||
|
@ -135,6 +161,13 @@ func (c *Client) Discover(ctx context.Context) (Directory, error) {
|
||||||
return *c.dir, nil
|
return *c.dir, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) directoryURL() string {
|
||||||
|
if c.DirectoryURL != "" {
|
||||||
|
return c.DirectoryURL
|
||||||
|
}
|
||||||
|
return LetsEncryptURL
|
||||||
|
}
|
||||||
|
|
||||||
// CreateCert requests a new certificate using the Certificate Signing Request csr encoded in DER format.
|
// CreateCert requests a new certificate using the Certificate Signing Request csr encoded in DER format.
|
||||||
// The exp argument indicates the desired certificate validity duration. CA may issue a certificate
|
// The exp argument indicates the desired certificate validity duration. CA may issue a certificate
|
||||||
// with a different duration.
|
// with a different duration.
|
||||||
|
@ -142,7 +175,7 @@ func (c *Client) Discover(ctx context.Context) (Directory, error) {
|
||||||
//
|
//
|
||||||
// In the case where CA server does not provide the issued certificate in the response,
|
// In the case where CA server does not provide the issued certificate in the response,
|
||||||
// CreateCert will poll certURL using c.FetchCert, which will result in additional round-trips.
|
// CreateCert will poll certURL using c.FetchCert, which will result in additional round-trips.
|
||||||
// In such scenario the caller can cancel the polling with ctx.
|
// In such a scenario, the caller can cancel the polling with ctx.
|
||||||
//
|
//
|
||||||
// CreateCert returns an error if the CA's response or chain was unreasonably large.
|
// CreateCert returns an error if the CA's response or chain was unreasonably large.
|
||||||
// Callers are encouraged to parse the returned value to ensure the certificate is valid and has the expected features.
|
// Callers are encouraged to parse the returned value to ensure the certificate is valid and has the expected features.
|
||||||
|
@ -166,14 +199,11 @@ func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration,
|
||||||
req.NotAfter = now.Add(exp).Format(time.RFC3339)
|
req.NotAfter = now.Add(exp).Format(time.RFC3339)
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := c.retryPostJWS(ctx, c.Key, c.dir.CertURL, req)
|
res, err := c.post(ctx, c.Key, c.dir.CertURL, req, wantStatus(http.StatusCreated))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode != http.StatusCreated {
|
|
||||||
return nil, "", responseError(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
curl := res.Header.Get("Location") // cert permanent URL
|
curl := res.Header.Get("Location") // cert permanent URL
|
||||||
if res.ContentLength == 0 {
|
if res.ContentLength == 0 {
|
||||||
|
@ -196,26 +226,11 @@ func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration,
|
||||||
// Callers are encouraged to parse the returned value to ensure the certificate is valid
|
// Callers are encouraged to parse the returned value to ensure the certificate is valid
|
||||||
// and has expected features.
|
// and has expected features.
|
||||||
func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]byte, error) {
|
func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]byte, error) {
|
||||||
for {
|
res, err := c.get(ctx, url, wantStatus(http.StatusOK))
|
||||||
res, err := c.get(ctx, url)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
|
||||||
if res.StatusCode == http.StatusOK {
|
|
||||||
return c.responseCert(ctx, res, bundle)
|
return c.responseCert(ctx, res, bundle)
|
||||||
}
|
|
||||||
if res.StatusCode > 299 {
|
|
||||||
return nil, responseError(res)
|
|
||||||
}
|
|
||||||
d := retryAfter(res.Header.Get("Retry-After"), 3*time.Second)
|
|
||||||
select {
|
|
||||||
case <-time.After(d):
|
|
||||||
// retry
|
|
||||||
case <-ctx.Done():
|
|
||||||
return nil, ctx.Err()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RevokeCert revokes a previously issued certificate cert, provided in DER format.
|
// RevokeCert revokes a previously issued certificate cert, provided in DER format.
|
||||||
|
@ -241,14 +256,11 @@ func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte,
|
||||||
if key == nil {
|
if key == nil {
|
||||||
key = c.Key
|
key = c.Key
|
||||||
}
|
}
|
||||||
res, err := c.retryPostJWS(ctx, key, c.dir.RevokeURL, body)
|
res, err := c.post(ctx, key, c.dir.RevokeURL, body, wantStatus(http.StatusOK))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode != http.StatusOK {
|
|
||||||
return responseError(res)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,7 +269,7 @@ func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte,
|
||||||
func AcceptTOS(tosURL string) bool { return true }
|
func AcceptTOS(tosURL string) bool { return true }
|
||||||
|
|
||||||
// Register creates a new account registration by following the "new-reg" flow.
|
// Register creates a new account registration by following the "new-reg" flow.
|
||||||
// It returns registered account. The account is not modified.
|
// It returns the registered account. The account is not modified.
|
||||||
//
|
//
|
||||||
// The registration may require the caller to agree to the CA's Terms of Service (TOS).
|
// The registration may require the caller to agree to the CA's Terms of Service (TOS).
|
||||||
// If so, and the account has not indicated the acceptance of the terms (see Account for details),
|
// If so, and the account has not indicated the acceptance of the terms (see Account for details),
|
||||||
|
@ -314,6 +326,20 @@ func (c *Client) UpdateReg(ctx context.Context, a *Account) (*Account, error) {
|
||||||
// a valid authorization (Authorization.Status is StatusValid). If so, the caller
|
// a valid authorization (Authorization.Status is StatusValid). If so, the caller
|
||||||
// need not fulfill any challenge and can proceed to requesting a certificate.
|
// need not fulfill any challenge and can proceed to requesting a certificate.
|
||||||
func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization, error) {
|
func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization, error) {
|
||||||
|
return c.authorize(ctx, "dns", domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthorizeIP is the same as Authorize but requests IP address authorization.
|
||||||
|
// Clients which successfully obtain such authorization may request to issue
|
||||||
|
// a certificate for IP addresses.
|
||||||
|
//
|
||||||
|
// See the ACME spec extension for more details about IP address identifiers:
|
||||||
|
// https://tools.ietf.org/html/draft-ietf-acme-ip.
|
||||||
|
func (c *Client) AuthorizeIP(ctx context.Context, ipaddr string) (*Authorization, error) {
|
||||||
|
return c.authorize(ctx, "ip", ipaddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) authorize(ctx context.Context, typ, val string) (*Authorization, error) {
|
||||||
if _, err := c.Discover(ctx); err != nil {
|
if _, err := c.Discover(ctx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -327,16 +353,13 @@ func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization,
|
||||||
Identifier authzID `json:"identifier"`
|
Identifier authzID `json:"identifier"`
|
||||||
}{
|
}{
|
||||||
Resource: "new-authz",
|
Resource: "new-authz",
|
||||||
Identifier: authzID{Type: "dns", Value: domain},
|
Identifier: authzID{Type: typ, Value: val},
|
||||||
}
|
}
|
||||||
res, err := c.retryPostJWS(ctx, c.Key, c.dir.AuthzURL, req)
|
res, err := c.post(ctx, c.Key, c.dir.AuthzURL, req, wantStatus(http.StatusCreated))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode != http.StatusCreated {
|
|
||||||
return nil, responseError(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
var v wireAuthz
|
var v wireAuthz
|
||||||
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
||||||
|
@ -353,14 +376,11 @@ func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization,
|
||||||
// If a caller needs to poll an authorization until its status is final,
|
// If a caller needs to poll an authorization until its status is final,
|
||||||
// see the WaitAuthorization method.
|
// see the WaitAuthorization method.
|
||||||
func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorization, error) {
|
func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorization, error) {
|
||||||
res, err := c.get(ctx, url)
|
res, err := c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted {
|
|
||||||
return nil, responseError(res)
|
|
||||||
}
|
|
||||||
var v wireAuthz
|
var v wireAuthz
|
||||||
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
||||||
return nil, fmt.Errorf("acme: invalid response: %v", err)
|
return nil, fmt.Errorf("acme: invalid response: %v", err)
|
||||||
|
@ -387,56 +407,58 @@ func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
|
||||||
Status: "deactivated",
|
Status: "deactivated",
|
||||||
Delete: true,
|
Delete: true,
|
||||||
}
|
}
|
||||||
res, err := c.retryPostJWS(ctx, c.Key, url, req)
|
res, err := c.post(ctx, c.Key, url, req, wantStatus(http.StatusOK))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode != http.StatusOK {
|
|
||||||
return responseError(res)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WaitAuthorization polls an authorization at the given URL
|
// WaitAuthorization polls an authorization at the given URL
|
||||||
// until it is in one of the final states, StatusValid or StatusInvalid,
|
// until it is in one of the final states, StatusValid or StatusInvalid,
|
||||||
// or the context is done.
|
// the ACME CA responded with a 4xx error code, or the context is done.
|
||||||
//
|
//
|
||||||
// It returns a non-nil Authorization only if its Status is StatusValid.
|
// It returns a non-nil Authorization only if its Status is StatusValid.
|
||||||
// In all other cases WaitAuthorization returns an error.
|
// In all other cases WaitAuthorization returns an error.
|
||||||
// If the Status is StatusInvalid, the returned error is of type *AuthorizationError.
|
// If the Status is StatusInvalid, the returned error is of type *AuthorizationError.
|
||||||
func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorization, error) {
|
func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorization, error) {
|
||||||
sleep := sleeper(ctx)
|
|
||||||
for {
|
for {
|
||||||
res, err := c.get(ctx, url)
|
res, err := c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
retry := res.Header.Get("Retry-After")
|
|
||||||
if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted {
|
|
||||||
res.Body.Close()
|
|
||||||
if err := sleep(retry, 1); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var raw wireAuthz
|
var raw wireAuthz
|
||||||
err = json.NewDecoder(res.Body).Decode(&raw)
|
err = json.NewDecoder(res.Body).Decode(&raw)
|
||||||
res.Body.Close()
|
res.Body.Close()
|
||||||
if err != nil {
|
switch {
|
||||||
if err := sleep(retry, 0); err != nil {
|
case err != nil:
|
||||||
return nil, err
|
// Skip and retry.
|
||||||
}
|
case raw.Status == StatusValid:
|
||||||
continue
|
|
||||||
}
|
|
||||||
if raw.Status == StatusValid {
|
|
||||||
return raw.authorization(url), nil
|
return raw.authorization(url), nil
|
||||||
}
|
case raw.Status == StatusInvalid:
|
||||||
if raw.Status == StatusInvalid {
|
|
||||||
return nil, raw.error(url)
|
return nil, raw.error(url)
|
||||||
}
|
}
|
||||||
if err := sleep(retry, 0); err != nil {
|
|
||||||
return nil, err
|
// Exponential backoff is implemented in c.get above.
|
||||||
|
// This is just to prevent continuously hitting the CA
|
||||||
|
// while waiting for a final authorization status.
|
||||||
|
d := retryAfter(res.Header.Get("Retry-After"))
|
||||||
|
if d == 0 {
|
||||||
|
// Given that the fastest challenges TLS-SNI and HTTP-01
|
||||||
|
// require a CA to make at least 1 network round trip
|
||||||
|
// and most likely persist a challenge state,
|
||||||
|
// this default delay seems reasonable.
|
||||||
|
d = time.Second
|
||||||
|
}
|
||||||
|
t := time.NewTimer(d)
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
t.Stop()
|
||||||
|
return nil, ctx.Err()
|
||||||
|
case <-t.C:
|
||||||
|
// Retry.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -445,14 +467,11 @@ func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorizat
|
||||||
//
|
//
|
||||||
// A client typically polls a challenge status using this method.
|
// A client typically polls a challenge status using this method.
|
||||||
func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, error) {
|
func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, error) {
|
||||||
res, err := c.get(ctx, url)
|
res, err := c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted {
|
|
||||||
return nil, responseError(res)
|
|
||||||
}
|
|
||||||
v := wireChallenge{URI: url}
|
v := wireChallenge{URI: url}
|
||||||
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
||||||
return nil, fmt.Errorf("acme: invalid response: %v", err)
|
return nil, fmt.Errorf("acme: invalid response: %v", err)
|
||||||
|
@ -479,16 +498,14 @@ func (c *Client) Accept(ctx context.Context, chal *Challenge) (*Challenge, error
|
||||||
Type: chal.Type,
|
Type: chal.Type,
|
||||||
Auth: auth,
|
Auth: auth,
|
||||||
}
|
}
|
||||||
res, err := c.retryPostJWS(ctx, c.Key, chal.URI, req)
|
res, err := c.post(ctx, c.Key, chal.URI, req, wantStatus(
|
||||||
|
http.StatusOK, // according to the spec
|
||||||
|
http.StatusAccepted, // Let's Encrypt: see https://goo.gl/WsJ7VT (acme-divergences.md)
|
||||||
|
))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
// Note: the protocol specifies 200 as the expected response code, but
|
|
||||||
// letsencrypt seems to be returning 202.
|
|
||||||
if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusAccepted {
|
|
||||||
return nil, responseError(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
var v wireChallenge
|
var v wireChallenge
|
||||||
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
|
||||||
|
@ -545,7 +562,7 @@ func (c *Client) HTTP01ChallengePath(token string) string {
|
||||||
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
|
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
|
||||||
//
|
//
|
||||||
// The returned certificate is valid for the next 24 hours and must be presented only when
|
// The returned certificate is valid for the next 24 hours and must be presented only when
|
||||||
// the server name of the client hello matches exactly the returned name value.
|
// the server name of the TLS ClientHello matches exactly the returned name value.
|
||||||
func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
|
func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
|
||||||
ka, err := keyAuth(c.Key.Public(), token)
|
ka, err := keyAuth(c.Key.Public(), token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -572,7 +589,7 @@ func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tl
|
||||||
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
|
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
|
||||||
//
|
//
|
||||||
// The returned certificate is valid for the next 24 hours and must be presented only when
|
// The returned certificate is valid for the next 24 hours and must be presented only when
|
||||||
// the server name in the client hello matches exactly the returned name value.
|
// the server name in the TLS ClientHello matches exactly the returned name value.
|
||||||
func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
|
func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
|
||||||
b := sha256.Sum256([]byte(token))
|
b := sha256.Sum256([]byte(token))
|
||||||
h := hex.EncodeToString(b[:])
|
h := hex.EncodeToString(b[:])
|
||||||
|
@ -593,6 +610,52 @@ func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tl
|
||||||
return cert, sanA, nil
|
return cert, sanA, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TLSALPN01ChallengeCert creates a certificate for TLS-ALPN-01 challenge response.
|
||||||
|
// Servers can present the certificate to validate the challenge and prove control
|
||||||
|
// over a domain name. For more details on TLS-ALPN-01 see
|
||||||
|
// https://tools.ietf.org/html/draft-shoemaker-acme-tls-alpn-00#section-3
|
||||||
|
//
|
||||||
|
// The token argument is a Challenge.Token value.
|
||||||
|
// If a WithKey option is provided, its private part signs the returned cert,
|
||||||
|
// and the public part is used to specify the signee.
|
||||||
|
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
|
||||||
|
//
|
||||||
|
// The returned certificate is valid for the next 24 hours and must be presented only when
|
||||||
|
// the server name in the TLS ClientHello matches the domain, and the special acme-tls/1 ALPN protocol
|
||||||
|
// has been specified.
|
||||||
|
func (c *Client) TLSALPN01ChallengeCert(token, domain string, opt ...CertOption) (cert tls.Certificate, err error) {
|
||||||
|
ka, err := keyAuth(c.Key.Public(), token)
|
||||||
|
if err != nil {
|
||||||
|
return tls.Certificate{}, err
|
||||||
|
}
|
||||||
|
shasum := sha256.Sum256([]byte(ka))
|
||||||
|
extValue, err := asn1.Marshal(shasum[:])
|
||||||
|
if err != nil {
|
||||||
|
return tls.Certificate{}, err
|
||||||
|
}
|
||||||
|
acmeExtension := pkix.Extension{
|
||||||
|
Id: idPeACMEIdentifierV1,
|
||||||
|
Critical: true,
|
||||||
|
Value: extValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl := defaultTLSChallengeCertTemplate()
|
||||||
|
|
||||||
|
var newOpt []CertOption
|
||||||
|
for _, o := range opt {
|
||||||
|
switch o := o.(type) {
|
||||||
|
case *certOptTemplate:
|
||||||
|
t := *(*x509.Certificate)(o) // shallow copy is ok
|
||||||
|
tmpl = &t
|
||||||
|
default:
|
||||||
|
newOpt = append(newOpt, o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmpl.ExtraExtensions = append(tmpl.ExtraExtensions, acmeExtension)
|
||||||
|
newOpt = append(newOpt, WithTemplate(tmpl))
|
||||||
|
return tlsChallengeCert([]string{domain}, newOpt)
|
||||||
|
}
|
||||||
|
|
||||||
// doReg sends all types of registration requests.
|
// doReg sends all types of registration requests.
|
||||||
// The type of request is identified by typ argument, which is a "resource"
|
// The type of request is identified by typ argument, which is a "resource"
|
||||||
// in the ACME spec terms.
|
// in the ACME spec terms.
|
||||||
|
@ -612,14 +675,15 @@ func (c *Client) doReg(ctx context.Context, url string, typ string, acct *Accoun
|
||||||
req.Contact = acct.Contact
|
req.Contact = acct.Contact
|
||||||
req.Agreement = acct.AgreedTerms
|
req.Agreement = acct.AgreedTerms
|
||||||
}
|
}
|
||||||
res, err := c.retryPostJWS(ctx, c.Key, url, req)
|
res, err := c.post(ctx, c.Key, url, req, wantStatus(
|
||||||
|
http.StatusOK, // updates and deletes
|
||||||
|
http.StatusCreated, // new account creation
|
||||||
|
http.StatusAccepted, // Let's Encrypt divergent implementation
|
||||||
|
))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode < 200 || res.StatusCode > 299 {
|
|
||||||
return nil, responseError(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
var v struct {
|
var v struct {
|
||||||
Contact []string
|
Contact []string
|
||||||
|
@ -649,66 +713,19 @@ func (c *Client) doReg(ctx context.Context, url string, typ string, acct *Accoun
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// retryPostJWS will retry calls to postJWS if there is a badNonce error,
|
|
||||||
// clearing the stored nonces after each error.
|
|
||||||
// If the response was 4XX-5XX, then responseError is called on the body,
|
|
||||||
// the body is closed, and the error returned.
|
|
||||||
func (c *Client) retryPostJWS(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, error) {
|
|
||||||
sleep := sleeper(ctx)
|
|
||||||
for {
|
|
||||||
res, err := c.postJWS(ctx, key, url, body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// handle errors 4XX-5XX with responseError
|
|
||||||
if res.StatusCode >= 400 && res.StatusCode <= 599 {
|
|
||||||
err := responseError(res)
|
|
||||||
res.Body.Close()
|
|
||||||
// according to spec badNonce is urn:ietf:params:acme:error:badNonce
|
|
||||||
// however, acme servers in the wild return their version of the error
|
|
||||||
// https://tools.ietf.org/html/draft-ietf-acme-acme-02#section-5.4
|
|
||||||
if ae, ok := err.(*Error); ok && strings.HasSuffix(strings.ToLower(ae.ProblemType), ":badnonce") {
|
|
||||||
// clear any nonces that we might've stored that might now be
|
|
||||||
// considered bad
|
|
||||||
c.clearNonces()
|
|
||||||
retry := res.Header.Get("Retry-After")
|
|
||||||
if err := sleep(retry, 1); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// postJWS signs the body with the given key and POSTs it to the provided url.
|
|
||||||
// The body argument must be JSON-serializable.
|
|
||||||
func (c *Client) postJWS(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, error) {
|
|
||||||
nonce, err := c.popNonce(ctx, url)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
b, err := jwsEncodeJSON(body, key, nonce)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
res, err := c.post(ctx, url, "application/jose+json", bytes.NewReader(b))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
c.addNonce(res.Header)
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// popNonce returns a nonce value previously stored with c.addNonce
|
// popNonce returns a nonce value previously stored with c.addNonce
|
||||||
// or fetches a fresh one from the given URL.
|
// or fetches a fresh one from a URL by issuing a HEAD request.
|
||||||
|
// It first tries c.directoryURL() and then the provided url if the former fails.
|
||||||
func (c *Client) popNonce(ctx context.Context, url string) (string, error) {
|
func (c *Client) popNonce(ctx context.Context, url string) (string, error) {
|
||||||
c.noncesMu.Lock()
|
c.noncesMu.Lock()
|
||||||
defer c.noncesMu.Unlock()
|
defer c.noncesMu.Unlock()
|
||||||
if len(c.nonces) == 0 {
|
if len(c.nonces) == 0 {
|
||||||
return c.fetchNonce(ctx, url)
|
dirURL := c.directoryURL()
|
||||||
|
v, err := c.fetchNonce(ctx, dirURL)
|
||||||
|
if err != nil && url != dirURL {
|
||||||
|
v, err = c.fetchNonce(ctx, url)
|
||||||
|
}
|
||||||
|
return v, err
|
||||||
}
|
}
|
||||||
var nonce string
|
var nonce string
|
||||||
for nonce = range c.nonces {
|
for nonce = range c.nonces {
|
||||||
|
@ -742,58 +759,12 @@ func (c *Client) addNonce(h http.Header) {
|
||||||
c.nonces[v] = struct{}{}
|
c.nonces[v] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) httpClient() *http.Client {
|
|
||||||
if c.HTTPClient != nil {
|
|
||||||
return c.HTTPClient
|
|
||||||
}
|
|
||||||
return http.DefaultClient
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) get(ctx context.Context, urlStr string) (*http.Response, error) {
|
|
||||||
req, err := http.NewRequest("GET", urlStr, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return c.do(ctx, req)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) head(ctx context.Context, urlStr string) (*http.Response, error) {
|
|
||||||
req, err := http.NewRequest("HEAD", urlStr, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return c.do(ctx, req)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) post(ctx context.Context, urlStr, contentType string, body io.Reader) (*http.Response, error) {
|
|
||||||
req, err := http.NewRequest("POST", urlStr, body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
req.Header.Set("Content-Type", contentType)
|
|
||||||
return c.do(ctx, req)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) do(ctx context.Context, req *http.Request) (*http.Response, error) {
|
|
||||||
res, err := c.httpClient().Do(req.WithContext(ctx))
|
|
||||||
if err != nil {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
// Prefer the unadorned context error.
|
|
||||||
// (The acme package had tests assuming this, previously from ctxhttp's
|
|
||||||
// behavior, predating net/http supporting contexts natively)
|
|
||||||
// TODO(bradfitz): reconsider this in the future. But for now this
|
|
||||||
// requires no test updates.
|
|
||||||
return nil, ctx.Err()
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) fetchNonce(ctx context.Context, url string) (string, error) {
|
func (c *Client) fetchNonce(ctx context.Context, url string) (string, error) {
|
||||||
resp, err := c.head(ctx, url)
|
r, err := http.NewRequest("HEAD", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
resp, err := c.doNoRetry(ctx, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -845,24 +816,6 @@ func (c *Client) responseCert(ctx context.Context, res *http.Response, bundle bo
|
||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// responseError creates an error of Error type from resp.
|
|
||||||
func responseError(resp *http.Response) error {
|
|
||||||
// don't care if ReadAll returns an error:
|
|
||||||
// json.Unmarshal will fail in that case anyway
|
|
||||||
b, _ := ioutil.ReadAll(resp.Body)
|
|
||||||
e := &wireError{Status: resp.StatusCode}
|
|
||||||
if err := json.Unmarshal(b, e); err != nil {
|
|
||||||
// this is not a regular error response:
|
|
||||||
// populate detail with anything we received,
|
|
||||||
// e.Status will already contain HTTP response code value
|
|
||||||
e.Detail = string(b)
|
|
||||||
if e.Detail == "" {
|
|
||||||
e.Detail = resp.Status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return e.error(resp.Header)
|
|
||||||
}
|
|
||||||
|
|
||||||
// chainCert fetches CA certificate chain recursively by following "up" links.
|
// chainCert fetches CA certificate chain recursively by following "up" links.
|
||||||
// Each recursive call increments the depth by 1, resulting in an error
|
// Each recursive call increments the depth by 1, resulting in an error
|
||||||
// if the recursion level reaches maxChainLen.
|
// if the recursion level reaches maxChainLen.
|
||||||
|
@ -873,14 +826,11 @@ func (c *Client) chainCert(ctx context.Context, url string, depth int) ([][]byte
|
||||||
return nil, errors.New("acme: certificate chain is too deep")
|
return nil, errors.New("acme: certificate chain is too deep")
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := c.get(ctx, url)
|
res, err := c.get(ctx, url, wantStatus(http.StatusOK))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
if res.StatusCode != http.StatusOK {
|
|
||||||
return nil, responseError(res)
|
|
||||||
}
|
|
||||||
b, err := ioutil.ReadAll(io.LimitReader(res.Body, maxCertSize+1))
|
b, err := ioutil.ReadAll(io.LimitReader(res.Body, maxCertSize+1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -925,65 +875,6 @@ func linkHeader(h http.Header, rel string) []string {
|
||||||
return links
|
return links
|
||||||
}
|
}
|
||||||
|
|
||||||
// sleeper returns a function that accepts the Retry-After HTTP header value
|
|
||||||
// and an increment that's used with backoff to increasingly sleep on
|
|
||||||
// consecutive calls until the context is done. If the Retry-After header
|
|
||||||
// cannot be parsed, then backoff is used with a maximum sleep time of 10
|
|
||||||
// seconds.
|
|
||||||
func sleeper(ctx context.Context) func(ra string, inc int) error {
|
|
||||||
var count int
|
|
||||||
return func(ra string, inc int) error {
|
|
||||||
count += inc
|
|
||||||
d := backoff(count, 10*time.Second)
|
|
||||||
d = retryAfter(ra, d)
|
|
||||||
wakeup := time.NewTimer(d)
|
|
||||||
defer wakeup.Stop()
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
case <-wakeup.C:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// retryAfter parses a Retry-After HTTP header value,
|
|
||||||
// trying to convert v into an int (seconds) or use http.ParseTime otherwise.
|
|
||||||
// It returns d if v cannot be parsed.
|
|
||||||
func retryAfter(v string, d time.Duration) time.Duration {
|
|
||||||
if i, err := strconv.Atoi(v); err == nil {
|
|
||||||
return time.Duration(i) * time.Second
|
|
||||||
}
|
|
||||||
t, err := http.ParseTime(v)
|
|
||||||
if err != nil {
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
return t.Sub(timeNow())
|
|
||||||
}
|
|
||||||
|
|
||||||
// backoff computes a duration after which an n+1 retry iteration should occur
|
|
||||||
// using truncated exponential backoff algorithm.
|
|
||||||
//
|
|
||||||
// The n argument is always bounded between 0 and 30.
|
|
||||||
// The max argument defines upper bound for the returned value.
|
|
||||||
func backoff(n int, max time.Duration) time.Duration {
|
|
||||||
if n < 0 {
|
|
||||||
n = 0
|
|
||||||
}
|
|
||||||
if n > 30 {
|
|
||||||
n = 30
|
|
||||||
}
|
|
||||||
var d time.Duration
|
|
||||||
if x, err := rand.Int(rand.Reader, big.NewInt(1000)); err == nil {
|
|
||||||
d = time.Duration(x.Int64()) * time.Millisecond
|
|
||||||
}
|
|
||||||
d += time.Duration(1<<uint(n)) * time.Second
|
|
||||||
if d > max {
|
|
||||||
return max
|
|
||||||
}
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
// keyAuth generates a key authorization string for a given token.
|
// keyAuth generates a key authorization string for a given token.
|
||||||
func keyAuth(pub crypto.PublicKey, token string) (string, error) {
|
func keyAuth(pub crypto.PublicKey, token string) (string, error) {
|
||||||
th, err := JWKThumbprint(pub)
|
th, err := JWKThumbprint(pub)
|
||||||
|
@ -993,14 +884,25 @@ func keyAuth(pub crypto.PublicKey, token string) (string, error) {
|
||||||
return fmt.Sprintf("%s.%s", token, th), nil
|
return fmt.Sprintf("%s.%s", token, th), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// defaultTLSChallengeCertTemplate is a template used to create challenge certs for TLS challenges.
|
||||||
|
func defaultTLSChallengeCertTemplate() *x509.Certificate {
|
||||||
|
return &x509.Certificate{
|
||||||
|
SerialNumber: big.NewInt(1),
|
||||||
|
NotBefore: time.Now(),
|
||||||
|
NotAfter: time.Now().Add(24 * time.Hour),
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// tlsChallengeCert creates a temporary certificate for TLS-SNI challenges
|
// tlsChallengeCert creates a temporary certificate for TLS-SNI challenges
|
||||||
// with the given SANs and auto-generated public/private key pair.
|
// with the given SANs and auto-generated public/private key pair.
|
||||||
|
// The Subject Common Name is set to the first SAN to aid debugging.
|
||||||
// To create a cert with a custom key pair, specify WithKey option.
|
// To create a cert with a custom key pair, specify WithKey option.
|
||||||
func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
|
func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
|
||||||
var (
|
var key crypto.Signer
|
||||||
key crypto.Signer
|
tmpl := defaultTLSChallengeCertTemplate()
|
||||||
tmpl *x509.Certificate
|
|
||||||
)
|
|
||||||
for _, o := range opt {
|
for _, o := range opt {
|
||||||
switch o := o.(type) {
|
switch o := o.(type) {
|
||||||
case *certOptKey:
|
case *certOptKey:
|
||||||
|
@ -1009,7 +911,7 @@ func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
|
||||||
}
|
}
|
||||||
key = o.key
|
key = o.key
|
||||||
case *certOptTemplate:
|
case *certOptTemplate:
|
||||||
var t = *(*x509.Certificate)(o) // shallow copy is ok
|
t := *(*x509.Certificate)(o) // shallow copy is ok
|
||||||
tmpl = &t
|
tmpl = &t
|
||||||
default:
|
default:
|
||||||
// package's fault, if we let this happen:
|
// package's fault, if we let this happen:
|
||||||
|
@ -1022,17 +924,10 @@ func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
|
||||||
return tls.Certificate{}, err
|
return tls.Certificate{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if tmpl == nil {
|
|
||||||
tmpl = &x509.Certificate{
|
|
||||||
SerialNumber: big.NewInt(1),
|
|
||||||
NotBefore: time.Now(),
|
|
||||||
NotAfter: time.Now().Add(24 * time.Hour),
|
|
||||||
BasicConstraintsValid: true,
|
|
||||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
|
||||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tmpl.DNSNames = san
|
tmpl.DNSNames = san
|
||||||
|
if len(san) > 0 {
|
||||||
|
tmpl.Subject.CommonName = san[0]
|
||||||
|
}
|
||||||
|
|
||||||
der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, key.Public(), key)
|
der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, key.Public(), key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
636
vendor/golang.org/x/crypto/acme/autocert/autocert.go
generated
vendored
636
vendor/golang.org/x/crypto/acme/autocert/autocert.go
generated
vendored
|
@ -24,8 +24,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
mathrand "math/rand"
|
mathrand "math/rand"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -43,7 +44,7 @@ var createCertRetryAfter = time.Minute
|
||||||
var pseudoRand *lockedMathRand
|
var pseudoRand *lockedMathRand
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
src := mathrand.NewSource(timeNow().UnixNano())
|
src := mathrand.NewSource(time.Now().UnixNano())
|
||||||
pseudoRand = &lockedMathRand{rnd: mathrand.New(src)}
|
pseudoRand = &lockedMathRand{rnd: mathrand.New(src)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +69,7 @@ func HostWhitelist(hosts ...string) HostPolicy {
|
||||||
}
|
}
|
||||||
return func(_ context.Context, host string) error {
|
return func(_ context.Context, host string) error {
|
||||||
if !whitelist[host] {
|
if !whitelist[host] {
|
||||||
return errors.New("acme/autocert: host not configured")
|
return fmt.Errorf("acme/autocert: host %q not configured in HostWhitelist", host)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -80,7 +81,8 @@ func defaultHostPolicy(context.Context, string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manager is a stateful certificate manager built on top of acme.Client.
|
// Manager is a stateful certificate manager built on top of acme.Client.
|
||||||
// It obtains and refreshes certificates automatically,
|
// It obtains and refreshes certificates automatically using "tls-alpn-01",
|
||||||
|
// "tls-sni-01", "tls-sni-02" and "http-01" challenge types,
|
||||||
// as well as providing them to a TLS server via tls.Config.
|
// as well as providing them to a TLS server via tls.Config.
|
||||||
//
|
//
|
||||||
// You must specify a cache implementation, such as DirCache,
|
// You must specify a cache implementation, such as DirCache,
|
||||||
|
@ -96,11 +98,11 @@ type Manager struct {
|
||||||
// To always accept the terms, the callers can use AcceptTOS.
|
// To always accept the terms, the callers can use AcceptTOS.
|
||||||
Prompt func(tosURL string) bool
|
Prompt func(tosURL string) bool
|
||||||
|
|
||||||
// Cache optionally stores and retrieves previously-obtained certificates.
|
// Cache optionally stores and retrieves previously-obtained certificates
|
||||||
// If nil, certs will only be cached for the lifetime of the Manager.
|
// and other state. If nil, certs will only be cached for the lifetime of
|
||||||
|
// the Manager. Multiple Managers can share the same Cache.
|
||||||
//
|
//
|
||||||
// Manager passes the Cache certificates data encoded in PEM, with private/public
|
// Using a persistent Cache, such as DirCache, is strongly recommended.
|
||||||
// parts combined in a single Cache.Put call, private key first.
|
|
||||||
Cache Cache
|
Cache Cache
|
||||||
|
|
||||||
// HostPolicy controls which domains the Manager will attempt
|
// HostPolicy controls which domains the Manager will attempt
|
||||||
|
@ -125,8 +127,10 @@ type Manager struct {
|
||||||
|
|
||||||
// Client is used to perform low-level operations, such as account registration
|
// Client is used to perform low-level operations, such as account registration
|
||||||
// and requesting new certificates.
|
// and requesting new certificates.
|
||||||
|
//
|
||||||
// If Client is nil, a zero-value acme.Client is used with acme.LetsEncryptURL
|
// If Client is nil, a zero-value acme.Client is used with acme.LetsEncryptURL
|
||||||
// directory endpoint and a newly-generated ECDSA P-256 key.
|
// as directory endpoint. If the Client.Key is nil, a new ECDSA P-256 key is
|
||||||
|
// generated and, if Cache is not nil, stored in cache.
|
||||||
//
|
//
|
||||||
// Mutating the field after the first call of GetCertificate method will have no effect.
|
// Mutating the field after the first call of GetCertificate method will have no effect.
|
||||||
Client *acme.Client
|
Client *acme.Client
|
||||||
|
@ -138,37 +142,95 @@ type Manager struct {
|
||||||
// If the Client's account key is already registered, Email is not used.
|
// If the Client's account key is already registered, Email is not used.
|
||||||
Email string
|
Email string
|
||||||
|
|
||||||
// ForceRSA makes the Manager generate certificates with 2048-bit RSA keys.
|
// ForceRSA used to make the Manager generate RSA certificates. It is now ignored.
|
||||||
//
|
//
|
||||||
// If false, a default is used. Currently the default
|
// Deprecated: the Manager will request the correct type of certificate based
|
||||||
// is EC-based keys using the P-256 curve.
|
// on what each client supports.
|
||||||
ForceRSA bool
|
ForceRSA bool
|
||||||
|
|
||||||
|
// ExtraExtensions are used when generating a new CSR (Certificate Request),
|
||||||
|
// thus allowing customization of the resulting certificate.
|
||||||
|
// For instance, TLS Feature Extension (RFC 7633) can be used
|
||||||
|
// to prevent an OCSP downgrade attack.
|
||||||
|
//
|
||||||
|
// The field value is passed to crypto/x509.CreateCertificateRequest
|
||||||
|
// in the template's ExtraExtensions field as is.
|
||||||
|
ExtraExtensions []pkix.Extension
|
||||||
|
|
||||||
clientMu sync.Mutex
|
clientMu sync.Mutex
|
||||||
client *acme.Client // initialized by acmeClient method
|
client *acme.Client // initialized by acmeClient method
|
||||||
|
|
||||||
stateMu sync.Mutex
|
stateMu sync.Mutex
|
||||||
state map[string]*certState // keyed by domain name
|
state map[certKey]*certState
|
||||||
|
|
||||||
// tokenCert is keyed by token domain name, which matches server name
|
|
||||||
// of ClientHello. Keys always have ".acme.invalid" suffix.
|
|
||||||
tokenCertMu sync.RWMutex
|
|
||||||
tokenCert map[string]*tls.Certificate
|
|
||||||
|
|
||||||
// renewal tracks the set of domains currently running renewal timers.
|
// renewal tracks the set of domains currently running renewal timers.
|
||||||
// It is keyed by domain name.
|
|
||||||
renewalMu sync.Mutex
|
renewalMu sync.Mutex
|
||||||
renewal map[string]*domainRenewal
|
renewal map[certKey]*domainRenewal
|
||||||
|
|
||||||
|
// tokensMu guards the rest of the fields: tryHTTP01, certTokens and httpTokens.
|
||||||
|
tokensMu sync.RWMutex
|
||||||
|
// tryHTTP01 indicates whether the Manager should try "http-01" challenge type
|
||||||
|
// during the authorization flow.
|
||||||
|
tryHTTP01 bool
|
||||||
|
// httpTokens contains response body values for http-01 challenges
|
||||||
|
// and is keyed by the URL path at which a challenge response is expected
|
||||||
|
// to be provisioned.
|
||||||
|
// The entries are stored for the duration of the authorization flow.
|
||||||
|
httpTokens map[string][]byte
|
||||||
|
// certTokens contains temporary certificates for tls-sni and tls-alpn challenges
|
||||||
|
// and is keyed by token domain name, which matches server name of ClientHello.
|
||||||
|
// Keys always have ".acme.invalid" suffix for tls-sni. Otherwise, they are domain names
|
||||||
|
// for tls-alpn.
|
||||||
|
// The entries are stored for the duration of the authorization flow.
|
||||||
|
certTokens map[string]*tls.Certificate
|
||||||
|
// nowFunc, if not nil, returns the current time. This may be set for
|
||||||
|
// testing purposes.
|
||||||
|
nowFunc func() time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// certKey is the key by which certificates are tracked in state, renewal and cache.
|
||||||
|
type certKey struct {
|
||||||
|
domain string // without trailing dot
|
||||||
|
isRSA bool // RSA cert for legacy clients (as opposed to default ECDSA)
|
||||||
|
isToken bool // tls-based challenge token cert; key type is undefined regardless of isRSA
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c certKey) String() string {
|
||||||
|
if c.isToken {
|
||||||
|
return c.domain + "+token"
|
||||||
|
}
|
||||||
|
if c.isRSA {
|
||||||
|
return c.domain + "+rsa"
|
||||||
|
}
|
||||||
|
return c.domain
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLSConfig creates a new TLS config suitable for net/http.Server servers,
|
||||||
|
// supporting HTTP/2 and the tls-alpn-01 ACME challenge type.
|
||||||
|
func (m *Manager) TLSConfig() *tls.Config {
|
||||||
|
return &tls.Config{
|
||||||
|
GetCertificate: m.GetCertificate,
|
||||||
|
NextProtos: []string{
|
||||||
|
"h2", "http/1.1", // enable HTTP/2
|
||||||
|
acme.ALPNProto, // enable tls-alpn ACME challenges
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCertificate implements the tls.Config.GetCertificate hook.
|
// GetCertificate implements the tls.Config.GetCertificate hook.
|
||||||
// It provides a TLS certificate for hello.ServerName host, including answering
|
// It provides a TLS certificate for hello.ServerName host, including answering
|
||||||
// *.acme.invalid (TLS-SNI) challenges. All other fields of hello are ignored.
|
// tls-alpn-01 and *.acme.invalid (tls-sni-01 and tls-sni-02) challenges.
|
||||||
|
// All other fields of hello are ignored.
|
||||||
//
|
//
|
||||||
// If m.HostPolicy is non-nil, GetCertificate calls the policy before requesting
|
// If m.HostPolicy is non-nil, GetCertificate calls the policy before requesting
|
||||||
// a new cert. A non-nil error returned from m.HostPolicy halts TLS negotiation.
|
// a new cert. A non-nil error returned from m.HostPolicy halts TLS negotiation.
|
||||||
// The error is propagated back to the caller of GetCertificate and is user-visible.
|
// The error is propagated back to the caller of GetCertificate and is user-visible.
|
||||||
// This does not affect cached certs. See HostPolicy field description for more details.
|
// This does not affect cached certs. See HostPolicy field description for more details.
|
||||||
|
//
|
||||||
|
// If GetCertificate is used directly, instead of via Manager.TLSConfig, package users will
|
||||||
|
// also have to add acme.ALPNProto to NextProtos for tls-alpn-01, or use HTTPHandler
|
||||||
|
// for http-01. (The tls-sni-* challenges have been deprecated by popular ACME providers
|
||||||
|
// due to security issues in the ecosystem.)
|
||||||
func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
if m.Prompt == nil {
|
if m.Prompt == nil {
|
||||||
return nil, errors.New("acme/autocert: Manager.Prompt not set")
|
return nil, errors.New("acme/autocert: Manager.Prompt not set")
|
||||||
|
@ -181,21 +243,26 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
|
||||||
if !strings.Contains(strings.Trim(name, "."), ".") {
|
if !strings.Contains(strings.Trim(name, "."), ".") {
|
||||||
return nil, errors.New("acme/autocert: server name component count invalid")
|
return nil, errors.New("acme/autocert: server name component count invalid")
|
||||||
}
|
}
|
||||||
if strings.ContainsAny(name, `/\`) {
|
if strings.ContainsAny(name, `+/\`) {
|
||||||
return nil, errors.New("acme/autocert: server name contains invalid character")
|
return nil, errors.New("acme/autocert: server name contains invalid character")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In the worst-case scenario, the timeout needs to account for caching, host policy,
|
||||||
|
// domain ownership verification and certificate issuance.
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
// check whether this is a token cert requested for TLS-SNI challenge
|
// Check whether this is a token cert requested for TLS-SNI or TLS-ALPN challenge.
|
||||||
if strings.HasSuffix(name, ".acme.invalid") {
|
if wantsTokenCert(hello) {
|
||||||
m.tokenCertMu.RLock()
|
m.tokensMu.RLock()
|
||||||
defer m.tokenCertMu.RUnlock()
|
defer m.tokensMu.RUnlock()
|
||||||
if cert := m.tokenCert[name]; cert != nil {
|
// It's ok to use the same token cert key for both tls-sni and tls-alpn
|
||||||
|
// because there's always at most 1 token cert per on-going domain authorization.
|
||||||
|
// See m.verify for details.
|
||||||
|
if cert := m.certTokens[name]; cert != nil {
|
||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
if cert, err := m.cacheGet(ctx, name); err == nil {
|
if cert, err := m.cacheGet(ctx, certKey{domain: name, isToken: true}); err == nil {
|
||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
// TODO: cache error results?
|
// TODO: cache error results?
|
||||||
|
@ -203,8 +270,11 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
|
||||||
}
|
}
|
||||||
|
|
||||||
// regular domain
|
// regular domain
|
||||||
name = strings.TrimSuffix(name, ".") // golang.org/issue/18114
|
ck := certKey{
|
||||||
cert, err := m.cert(ctx, name)
|
domain: strings.TrimSuffix(name, "."), // golang.org/issue/18114
|
||||||
|
isRSA: !supportsECDSA(hello),
|
||||||
|
}
|
||||||
|
cert, err := m.cert(ctx, ck)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
|
@ -216,27 +286,146 @@ func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate,
|
||||||
if err := m.hostPolicy()(ctx, name); err != nil {
|
if err := m.hostPolicy()(ctx, name); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
cert, err = m.createCert(ctx, name)
|
cert, err = m.createCert(ctx, ck)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
m.cachePut(ctx, name, cert)
|
m.cachePut(ctx, ck, cert)
|
||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// wantsTokenCert reports whether a TLS request with SNI is made by a CA server
|
||||||
|
// for a challenge verification.
|
||||||
|
func wantsTokenCert(hello *tls.ClientHelloInfo) bool {
|
||||||
|
// tls-alpn-01
|
||||||
|
if len(hello.SupportedProtos) == 1 && hello.SupportedProtos[0] == acme.ALPNProto {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// tls-sni-xx
|
||||||
|
return strings.HasSuffix(hello.ServerName, ".acme.invalid")
|
||||||
|
}
|
||||||
|
|
||||||
|
func supportsECDSA(hello *tls.ClientHelloInfo) bool {
|
||||||
|
// The "signature_algorithms" extension, if present, limits the key exchange
|
||||||
|
// algorithms allowed by the cipher suites. See RFC 5246, section 7.4.1.4.1.
|
||||||
|
if hello.SignatureSchemes != nil {
|
||||||
|
ecdsaOK := false
|
||||||
|
schemeLoop:
|
||||||
|
for _, scheme := range hello.SignatureSchemes {
|
||||||
|
const tlsECDSAWithSHA1 tls.SignatureScheme = 0x0203 // constant added in Go 1.10
|
||||||
|
switch scheme {
|
||||||
|
case tlsECDSAWithSHA1, tls.ECDSAWithP256AndSHA256,
|
||||||
|
tls.ECDSAWithP384AndSHA384, tls.ECDSAWithP521AndSHA512:
|
||||||
|
ecdsaOK = true
|
||||||
|
break schemeLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ecdsaOK {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hello.SupportedCurves != nil {
|
||||||
|
ecdsaOK := false
|
||||||
|
for _, curve := range hello.SupportedCurves {
|
||||||
|
if curve == tls.CurveP256 {
|
||||||
|
ecdsaOK = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ecdsaOK {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, suite := range hello.CipherSuites {
|
||||||
|
switch suite {
|
||||||
|
case tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPHandler configures the Manager to provision ACME "http-01" challenge responses.
|
||||||
|
// It returns an http.Handler that responds to the challenges and must be
|
||||||
|
// running on port 80. If it receives a request that is not an ACME challenge,
|
||||||
|
// it delegates the request to the optional fallback handler.
|
||||||
|
//
|
||||||
|
// If fallback is nil, the returned handler redirects all GET and HEAD requests
|
||||||
|
// to the default TLS port 443 with 302 Found status code, preserving the original
|
||||||
|
// request path and query. It responds with 400 Bad Request to all other HTTP methods.
|
||||||
|
// The fallback is not protected by the optional HostPolicy.
|
||||||
|
//
|
||||||
|
// Because the fallback handler is run with unencrypted port 80 requests,
|
||||||
|
// the fallback should not serve TLS-only requests.
|
||||||
|
//
|
||||||
|
// If HTTPHandler is never called, the Manager will only use the "tls-alpn-01"
|
||||||
|
// challenge for domain verification.
|
||||||
|
func (m *Manager) HTTPHandler(fallback http.Handler) http.Handler {
|
||||||
|
m.tokensMu.Lock()
|
||||||
|
defer m.tokensMu.Unlock()
|
||||||
|
m.tryHTTP01 = true
|
||||||
|
|
||||||
|
if fallback == nil {
|
||||||
|
fallback = http.HandlerFunc(handleHTTPRedirect)
|
||||||
|
}
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !strings.HasPrefix(r.URL.Path, "/.well-known/acme-challenge/") {
|
||||||
|
fallback.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// A reasonable context timeout for cache and host policy only,
|
||||||
|
// because we don't wait for a new certificate issuance here.
|
||||||
|
ctx, cancel := context.WithTimeout(r.Context(), time.Minute)
|
||||||
|
defer cancel()
|
||||||
|
if err := m.hostPolicy()(ctx, r.Host); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := m.httpToken(ctx, r.URL.Path)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Write(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleHTTPRedirect(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != "GET" && r.Method != "HEAD" {
|
||||||
|
http.Error(w, "Use HTTPS", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
target := "https://" + stripPort(r.Host) + r.URL.RequestURI()
|
||||||
|
http.Redirect(w, r, target, http.StatusFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
func stripPort(hostport string) string {
|
||||||
|
host, _, err := net.SplitHostPort(hostport)
|
||||||
|
if err != nil {
|
||||||
|
return hostport
|
||||||
|
}
|
||||||
|
return net.JoinHostPort(host, "443")
|
||||||
|
}
|
||||||
|
|
||||||
// cert returns an existing certificate either from m.state or cache.
|
// cert returns an existing certificate either from m.state or cache.
|
||||||
// If a certificate is found in cache but not in m.state, the latter will be filled
|
// If a certificate is found in cache but not in m.state, the latter will be filled
|
||||||
// with the cached value.
|
// with the cached value.
|
||||||
func (m *Manager) cert(ctx context.Context, name string) (*tls.Certificate, error) {
|
func (m *Manager) cert(ctx context.Context, ck certKey) (*tls.Certificate, error) {
|
||||||
m.stateMu.Lock()
|
m.stateMu.Lock()
|
||||||
if s, ok := m.state[name]; ok {
|
if s, ok := m.state[ck]; ok {
|
||||||
m.stateMu.Unlock()
|
m.stateMu.Unlock()
|
||||||
s.RLock()
|
s.RLock()
|
||||||
defer s.RUnlock()
|
defer s.RUnlock()
|
||||||
return s.tlscert()
|
return s.tlscert()
|
||||||
}
|
}
|
||||||
defer m.stateMu.Unlock()
|
defer m.stateMu.Unlock()
|
||||||
cert, err := m.cacheGet(ctx, name)
|
cert, err := m.cacheGet(ctx, ck)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -245,25 +434,25 @@ func (m *Manager) cert(ctx context.Context, name string) (*tls.Certificate, erro
|
||||||
return nil, errors.New("acme/autocert: private key cannot sign")
|
return nil, errors.New("acme/autocert: private key cannot sign")
|
||||||
}
|
}
|
||||||
if m.state == nil {
|
if m.state == nil {
|
||||||
m.state = make(map[string]*certState)
|
m.state = make(map[certKey]*certState)
|
||||||
}
|
}
|
||||||
s := &certState{
|
s := &certState{
|
||||||
key: signer,
|
key: signer,
|
||||||
cert: cert.Certificate,
|
cert: cert.Certificate,
|
||||||
leaf: cert.Leaf,
|
leaf: cert.Leaf,
|
||||||
}
|
}
|
||||||
m.state[name] = s
|
m.state[ck] = s
|
||||||
go m.renew(name, s.key, s.leaf.NotAfter)
|
go m.renew(ck, s.key, s.leaf.NotAfter)
|
||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// cacheGet always returns a valid certificate, or an error otherwise.
|
// cacheGet always returns a valid certificate, or an error otherwise.
|
||||||
// If a cached certficate exists but is not valid, ErrCacheMiss is returned.
|
// If a cached certificate exists but is not valid, ErrCacheMiss is returned.
|
||||||
func (m *Manager) cacheGet(ctx context.Context, domain string) (*tls.Certificate, error) {
|
func (m *Manager) cacheGet(ctx context.Context, ck certKey) (*tls.Certificate, error) {
|
||||||
if m.Cache == nil {
|
if m.Cache == nil {
|
||||||
return nil, ErrCacheMiss
|
return nil, ErrCacheMiss
|
||||||
}
|
}
|
||||||
data, err := m.Cache.Get(ctx, domain)
|
data, err := m.Cache.Get(ctx, ck.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -294,7 +483,7 @@ func (m *Manager) cacheGet(ctx context.Context, domain string) (*tls.Certificate
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify and create TLS cert
|
// verify and create TLS cert
|
||||||
leaf, err := validCert(domain, pubDER, privKey)
|
leaf, err := validCert(ck, pubDER, privKey, m.now())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ErrCacheMiss
|
return nil, ErrCacheMiss
|
||||||
}
|
}
|
||||||
|
@ -306,7 +495,7 @@ func (m *Manager) cacheGet(ctx context.Context, domain string) (*tls.Certificate
|
||||||
return tlscert, nil
|
return tlscert, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) cachePut(ctx context.Context, domain string, tlscert *tls.Certificate) error {
|
func (m *Manager) cachePut(ctx context.Context, ck certKey, tlscert *tls.Certificate) error {
|
||||||
if m.Cache == nil {
|
if m.Cache == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -338,7 +527,7 @@ func (m *Manager) cachePut(ctx context.Context, domain string, tlscert *tls.Cert
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.Cache.Put(ctx, domain, buf.Bytes())
|
return m.Cache.Put(ctx, ck.String(), buf.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeECDSAKey(w io.Writer, key *ecdsa.PrivateKey) error {
|
func encodeECDSAKey(w io.Writer, key *ecdsa.PrivateKey) error {
|
||||||
|
@ -355,9 +544,9 @@ func encodeECDSAKey(w io.Writer, key *ecdsa.PrivateKey) error {
|
||||||
//
|
//
|
||||||
// If the domain is already being verified, it waits for the existing verification to complete.
|
// If the domain is already being verified, it waits for the existing verification to complete.
|
||||||
// Either way, createCert blocks for the duration of the whole process.
|
// Either way, createCert blocks for the duration of the whole process.
|
||||||
func (m *Manager) createCert(ctx context.Context, domain string) (*tls.Certificate, error) {
|
func (m *Manager) createCert(ctx context.Context, ck certKey) (*tls.Certificate, error) {
|
||||||
// TODO: maybe rewrite this whole piece using sync.Once
|
// TODO: maybe rewrite this whole piece using sync.Once
|
||||||
state, err := m.certState(domain)
|
state, err := m.certState(ck)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -371,48 +560,48 @@ func (m *Manager) createCert(ctx context.Context, domain string) (*tls.Certifica
|
||||||
|
|
||||||
// We are the first; state is locked.
|
// We are the first; state is locked.
|
||||||
// Unblock the readers when domain ownership is verified
|
// Unblock the readers when domain ownership is verified
|
||||||
// and the we got the cert or the process failed.
|
// and we got the cert or the process failed.
|
||||||
defer state.Unlock()
|
defer state.Unlock()
|
||||||
state.locked = false
|
state.locked = false
|
||||||
|
|
||||||
der, leaf, err := m.authorizedCert(ctx, state.key, domain)
|
der, leaf, err := m.authorizedCert(ctx, state.key, ck)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Remove the failed state after some time,
|
// Remove the failed state after some time,
|
||||||
// making the manager call createCert again on the following TLS hello.
|
// making the manager call createCert again on the following TLS hello.
|
||||||
time.AfterFunc(createCertRetryAfter, func() {
|
time.AfterFunc(createCertRetryAfter, func() {
|
||||||
defer testDidRemoveState(domain)
|
defer testDidRemoveState(ck)
|
||||||
m.stateMu.Lock()
|
m.stateMu.Lock()
|
||||||
defer m.stateMu.Unlock()
|
defer m.stateMu.Unlock()
|
||||||
// Verify the state hasn't changed and it's still invalid
|
// Verify the state hasn't changed and it's still invalid
|
||||||
// before deleting.
|
// before deleting.
|
||||||
s, ok := m.state[domain]
|
s, ok := m.state[ck]
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if _, err := validCert(domain, s.cert, s.key); err == nil {
|
if _, err := validCert(ck, s.cert, s.key, m.now()); err == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
delete(m.state, domain)
|
delete(m.state, ck)
|
||||||
})
|
})
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
state.cert = der
|
state.cert = der
|
||||||
state.leaf = leaf
|
state.leaf = leaf
|
||||||
go m.renew(domain, state.key, state.leaf.NotAfter)
|
go m.renew(ck, state.key, state.leaf.NotAfter)
|
||||||
return state.tlscert()
|
return state.tlscert()
|
||||||
}
|
}
|
||||||
|
|
||||||
// certState returns a new or existing certState.
|
// certState returns a new or existing certState.
|
||||||
// If a new certState is returned, state.exist is false and the state is locked.
|
// If a new certState is returned, state.exist is false and the state is locked.
|
||||||
// The returned error is non-nil only in the case where a new state could not be created.
|
// The returned error is non-nil only in the case where a new state could not be created.
|
||||||
func (m *Manager) certState(domain string) (*certState, error) {
|
func (m *Manager) certState(ck certKey) (*certState, error) {
|
||||||
m.stateMu.Lock()
|
m.stateMu.Lock()
|
||||||
defer m.stateMu.Unlock()
|
defer m.stateMu.Unlock()
|
||||||
if m.state == nil {
|
if m.state == nil {
|
||||||
m.state = make(map[string]*certState)
|
m.state = make(map[certKey]*certState)
|
||||||
}
|
}
|
||||||
// existing state
|
// existing state
|
||||||
if state, ok := m.state[domain]; ok {
|
if state, ok := m.state[ck]; ok {
|
||||||
return state, nil
|
return state, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,7 +610,7 @@ func (m *Manager) certState(domain string) (*certState, error) {
|
||||||
err error
|
err error
|
||||||
key crypto.Signer
|
key crypto.Signer
|
||||||
)
|
)
|
||||||
if m.ForceRSA {
|
if ck.isRSA {
|
||||||
key, err = rsa.GenerateKey(rand.Reader, 2048)
|
key, err = rsa.GenerateKey(rand.Reader, 2048)
|
||||||
} else {
|
} else {
|
||||||
key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
@ -435,21 +624,22 @@ func (m *Manager) certState(domain string) (*certState, error) {
|
||||||
locked: true,
|
locked: true,
|
||||||
}
|
}
|
||||||
state.Lock() // will be unlocked by m.certState caller
|
state.Lock() // will be unlocked by m.certState caller
|
||||||
m.state[domain] = state
|
m.state[ck] = state
|
||||||
return state, nil
|
return state, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// authorizedCert starts domain ownership verification process and requests a new cert upon success.
|
// authorizedCert starts the domain ownership verification process and requests a new cert upon success.
|
||||||
// The key argument is the certificate private key.
|
// The key argument is the certificate private key.
|
||||||
func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, domain string) (der [][]byte, leaf *x509.Certificate, err error) {
|
func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, ck certKey) (der [][]byte, leaf *x509.Certificate, err error) {
|
||||||
if err := m.verify(ctx, domain); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
client, err := m.acmeClient(ctx)
|
client, err := m.acmeClient(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
csr, err := certRequest(key, domain)
|
|
||||||
|
if err := m.verify(ctx, client, ck.domain); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
csr, err := certRequest(key, ck.domain, m.ExtraExtensions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -457,105 +647,227 @@ func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, domain
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
leaf, err = validCert(domain, der, key)
|
leaf, err = validCert(ck, der, key, m.now())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
return der, leaf, nil
|
return der, leaf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify starts a new identifier (domain) authorization flow.
|
// revokePendingAuthz revokes all authorizations idenfied by the elements of uri slice.
|
||||||
// It prepares a challenge response and then blocks until the authorization
|
// It ignores revocation errors.
|
||||||
// is marked as "completed" by the CA (either succeeded or failed).
|
func (m *Manager) revokePendingAuthz(ctx context.Context, uri []string) {
|
||||||
//
|
|
||||||
// verify returns nil iff the verification was successful.
|
|
||||||
func (m *Manager) verify(ctx context.Context, domain string) error {
|
|
||||||
client, err := m.acmeClient(ctx)
|
client, err := m.acmeClient(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return
|
||||||
}
|
}
|
||||||
|
for _, u := range uri {
|
||||||
|
client.RevokeAuthorization(ctx, u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// start domain authorization and get the challenge
|
// verify runs the identifier (domain) authorization flow
|
||||||
|
// using each applicable ACME challenge type.
|
||||||
|
func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string) error {
|
||||||
|
// The list of challenge types we'll try to fulfill
|
||||||
|
// in this specific order.
|
||||||
|
challengeTypes := []string{"tls-alpn-01", "tls-sni-02", "tls-sni-01"}
|
||||||
|
m.tokensMu.RLock()
|
||||||
|
if m.tryHTTP01 {
|
||||||
|
challengeTypes = append(challengeTypes, "http-01")
|
||||||
|
}
|
||||||
|
m.tokensMu.RUnlock()
|
||||||
|
|
||||||
|
// Keep track of pending authzs and revoke the ones that did not validate.
|
||||||
|
pendingAuthzs := make(map[string]bool)
|
||||||
|
defer func() {
|
||||||
|
var uri []string
|
||||||
|
for k, pending := range pendingAuthzs {
|
||||||
|
if pending {
|
||||||
|
uri = append(uri, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(uri) > 0 {
|
||||||
|
// Use "detached" background context.
|
||||||
|
// The revocations need not happen in the current verification flow.
|
||||||
|
go m.revokePendingAuthz(context.Background(), uri)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// errs accumulates challenge failure errors, printed if all fail
|
||||||
|
errs := make(map[*acme.Challenge]error)
|
||||||
|
var nextTyp int // challengeType index of the next challenge type to try
|
||||||
|
for {
|
||||||
|
// Start domain authorization and get the challenge.
|
||||||
authz, err := client.Authorize(ctx, domain)
|
authz, err := client.Authorize(ctx, domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// maybe don't need to at all
|
// No point in accepting challenges if the authorization status
|
||||||
if authz.Status == acme.StatusValid {
|
// is in a final state.
|
||||||
return nil
|
switch authz.Status {
|
||||||
|
case acme.StatusValid:
|
||||||
|
return nil // already authorized
|
||||||
|
case acme.StatusInvalid:
|
||||||
|
return fmt.Errorf("acme/autocert: invalid authorization %q", authz.URI)
|
||||||
}
|
}
|
||||||
|
|
||||||
// pick a challenge: prefer tls-sni-02 over tls-sni-01
|
pendingAuthzs[authz.URI] = true
|
||||||
// TODO: consider authz.Combinations
|
|
||||||
|
// Pick the next preferred challenge.
|
||||||
var chal *acme.Challenge
|
var chal *acme.Challenge
|
||||||
for _, c := range authz.Challenges {
|
for chal == nil && nextTyp < len(challengeTypes) {
|
||||||
if c.Type == "tls-sni-02" {
|
chal = pickChallenge(challengeTypes[nextTyp], authz.Challenges)
|
||||||
chal = c
|
nextTyp++
|
||||||
break
|
|
||||||
}
|
|
||||||
if c.Type == "tls-sni-01" {
|
|
||||||
chal = c
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if chal == nil {
|
if chal == nil {
|
||||||
return errors.New("acme/autocert: no supported challenge type found")
|
errorMsg := fmt.Sprintf("acme/autocert: unable to authorize %q", domain)
|
||||||
|
for chal, err := range errs {
|
||||||
|
errorMsg += fmt.Sprintf("; challenge %q failed with error: %v", chal.Type, err)
|
||||||
}
|
}
|
||||||
|
return errors.New(errorMsg)
|
||||||
// create a token cert for the challenge response
|
|
||||||
var (
|
|
||||||
cert tls.Certificate
|
|
||||||
name string
|
|
||||||
)
|
|
||||||
switch chal.Type {
|
|
||||||
case "tls-sni-01":
|
|
||||||
cert, name, err = client.TLSSNI01ChallengeCert(chal.Token)
|
|
||||||
case "tls-sni-02":
|
|
||||||
cert, name, err = client.TLSSNI02ChallengeCert(chal.Token)
|
|
||||||
default:
|
|
||||||
err = fmt.Errorf("acme/autocert: unknown challenge type %q", chal.Type)
|
|
||||||
}
|
}
|
||||||
|
cleanup, err := m.fulfill(ctx, client, chal, domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
errs[chal] = err
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
m.putTokenCert(ctx, name, &cert)
|
defer cleanup()
|
||||||
defer func() {
|
|
||||||
// verification has ended at this point
|
|
||||||
// don't need token cert anymore
|
|
||||||
go m.deleteTokenCert(name)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// ready to fulfill the challenge
|
|
||||||
if _, err := client.Accept(ctx, chal); err != nil {
|
if _, err := client.Accept(ctx, chal); err != nil {
|
||||||
return err
|
errs[chal] = err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// A challenge is fulfilled and accepted: wait for the CA to validate.
|
||||||
|
if _, err := client.WaitAuthorization(ctx, authz.URI); err != nil {
|
||||||
|
errs[chal] = err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
delete(pendingAuthzs, authz.URI)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
// wait for the CA to validate
|
|
||||||
_, err = client.WaitAuthorization(ctx, authz.URI)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// putTokenCert stores the cert under the named key in both m.tokenCert map
|
// fulfill provisions a response to the challenge chal.
|
||||||
// and m.Cache.
|
// The cleanup is non-nil only if provisioning succeeded.
|
||||||
func (m *Manager) putTokenCert(ctx context.Context, name string, cert *tls.Certificate) {
|
func (m *Manager) fulfill(ctx context.Context, client *acme.Client, chal *acme.Challenge, domain string) (cleanup func(), err error) {
|
||||||
m.tokenCertMu.Lock()
|
switch chal.Type {
|
||||||
defer m.tokenCertMu.Unlock()
|
case "tls-alpn-01":
|
||||||
if m.tokenCert == nil {
|
cert, err := client.TLSALPN01ChallengeCert(chal.Token, domain)
|
||||||
m.tokenCert = make(map[string]*tls.Certificate)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
m.tokenCert[name] = cert
|
m.putCertToken(ctx, domain, &cert)
|
||||||
m.cachePut(ctx, name, cert)
|
return func() { go m.deleteCertToken(domain) }, nil
|
||||||
|
case "tls-sni-01":
|
||||||
|
cert, name, err := client.TLSSNI01ChallengeCert(chal.Token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m.putCertToken(ctx, name, &cert)
|
||||||
|
return func() { go m.deleteCertToken(name) }, nil
|
||||||
|
case "tls-sni-02":
|
||||||
|
cert, name, err := client.TLSSNI02ChallengeCert(chal.Token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m.putCertToken(ctx, name, &cert)
|
||||||
|
return func() { go m.deleteCertToken(name) }, nil
|
||||||
|
case "http-01":
|
||||||
|
resp, err := client.HTTP01ChallengeResponse(chal.Token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p := client.HTTP01ChallengePath(chal.Token)
|
||||||
|
m.putHTTPToken(ctx, p, resp)
|
||||||
|
return func() { go m.deleteHTTPToken(p) }, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("acme/autocert: unknown challenge type %q", chal.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteTokenCert removes the token certificate for the specified domain name
|
func pickChallenge(typ string, chal []*acme.Challenge) *acme.Challenge {
|
||||||
// from both m.tokenCert map and m.Cache.
|
for _, c := range chal {
|
||||||
func (m *Manager) deleteTokenCert(name string) {
|
if c.Type == typ {
|
||||||
m.tokenCertMu.Lock()
|
return c
|
||||||
defer m.tokenCertMu.Unlock()
|
}
|
||||||
delete(m.tokenCert, name)
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// putCertToken stores the token certificate with the specified name
|
||||||
|
// in both m.certTokens map and m.Cache.
|
||||||
|
func (m *Manager) putCertToken(ctx context.Context, name string, cert *tls.Certificate) {
|
||||||
|
m.tokensMu.Lock()
|
||||||
|
defer m.tokensMu.Unlock()
|
||||||
|
if m.certTokens == nil {
|
||||||
|
m.certTokens = make(map[string]*tls.Certificate)
|
||||||
|
}
|
||||||
|
m.certTokens[name] = cert
|
||||||
|
m.cachePut(ctx, certKey{domain: name, isToken: true}, cert)
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteCertToken removes the token certificate with the specified name
|
||||||
|
// from both m.certTokens map and m.Cache.
|
||||||
|
func (m *Manager) deleteCertToken(name string) {
|
||||||
|
m.tokensMu.Lock()
|
||||||
|
defer m.tokensMu.Unlock()
|
||||||
|
delete(m.certTokens, name)
|
||||||
if m.Cache != nil {
|
if m.Cache != nil {
|
||||||
m.Cache.Delete(context.Background(), name)
|
ck := certKey{domain: name, isToken: true}
|
||||||
|
m.Cache.Delete(context.Background(), ck.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// httpToken retrieves an existing http-01 token value from an in-memory map
|
||||||
|
// or the optional cache.
|
||||||
|
func (m *Manager) httpToken(ctx context.Context, tokenPath string) ([]byte, error) {
|
||||||
|
m.tokensMu.RLock()
|
||||||
|
defer m.tokensMu.RUnlock()
|
||||||
|
if v, ok := m.httpTokens[tokenPath]; ok {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
if m.Cache == nil {
|
||||||
|
return nil, fmt.Errorf("acme/autocert: no token at %q", tokenPath)
|
||||||
|
}
|
||||||
|
return m.Cache.Get(ctx, httpTokenCacheKey(tokenPath))
|
||||||
|
}
|
||||||
|
|
||||||
|
// putHTTPToken stores an http-01 token value using tokenPath as key
|
||||||
|
// in both in-memory map and the optional Cache.
|
||||||
|
//
|
||||||
|
// It ignores any error returned from Cache.Put.
|
||||||
|
func (m *Manager) putHTTPToken(ctx context.Context, tokenPath, val string) {
|
||||||
|
m.tokensMu.Lock()
|
||||||
|
defer m.tokensMu.Unlock()
|
||||||
|
if m.httpTokens == nil {
|
||||||
|
m.httpTokens = make(map[string][]byte)
|
||||||
|
}
|
||||||
|
b := []byte(val)
|
||||||
|
m.httpTokens[tokenPath] = b
|
||||||
|
if m.Cache != nil {
|
||||||
|
m.Cache.Put(ctx, httpTokenCacheKey(tokenPath), b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteHTTPToken removes an http-01 token value from both in-memory map
|
||||||
|
// and the optional Cache, ignoring any error returned from the latter.
|
||||||
|
//
|
||||||
|
// If m.Cache is non-nil, it blocks until Cache.Delete returns without a timeout.
|
||||||
|
func (m *Manager) deleteHTTPToken(tokenPath string) {
|
||||||
|
m.tokensMu.Lock()
|
||||||
|
defer m.tokensMu.Unlock()
|
||||||
|
delete(m.httpTokens, tokenPath)
|
||||||
|
if m.Cache != nil {
|
||||||
|
m.Cache.Delete(context.Background(), httpTokenCacheKey(tokenPath))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// httpTokenCacheKey returns a key at which an http-01 token value may be stored
|
||||||
|
// in the Manager's optional Cache.
|
||||||
|
func httpTokenCacheKey(tokenPath string) string {
|
||||||
|
return path.Base(tokenPath) + "+http-01"
|
||||||
|
}
|
||||||
|
|
||||||
// renew starts a cert renewal timer loop, one per domain.
|
// renew starts a cert renewal timer loop, one per domain.
|
||||||
//
|
//
|
||||||
// The loop is scheduled in two cases:
|
// The loop is scheduled in two cases:
|
||||||
|
@ -564,18 +876,18 @@ func (m *Manager) deleteTokenCert(name string) {
|
||||||
//
|
//
|
||||||
// The key argument is a certificate private key.
|
// The key argument is a certificate private key.
|
||||||
// The exp argument is the cert expiration time (NotAfter).
|
// The exp argument is the cert expiration time (NotAfter).
|
||||||
func (m *Manager) renew(domain string, key crypto.Signer, exp time.Time) {
|
func (m *Manager) renew(ck certKey, key crypto.Signer, exp time.Time) {
|
||||||
m.renewalMu.Lock()
|
m.renewalMu.Lock()
|
||||||
defer m.renewalMu.Unlock()
|
defer m.renewalMu.Unlock()
|
||||||
if m.renewal[domain] != nil {
|
if m.renewal[ck] != nil {
|
||||||
// another goroutine is already on it
|
// another goroutine is already on it
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if m.renewal == nil {
|
if m.renewal == nil {
|
||||||
m.renewal = make(map[string]*domainRenewal)
|
m.renewal = make(map[certKey]*domainRenewal)
|
||||||
}
|
}
|
||||||
dr := &domainRenewal{m: m, domain: domain, key: key}
|
dr := &domainRenewal{m: m, ck: ck, key: key}
|
||||||
m.renewal[domain] = dr
|
m.renewal[ck] = dr
|
||||||
dr.start(exp)
|
dr.start(exp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -591,7 +903,10 @@ func (m *Manager) stopRenew() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) accountKey(ctx context.Context) (crypto.Signer, error) {
|
func (m *Manager) accountKey(ctx context.Context) (crypto.Signer, error) {
|
||||||
const keyName = "acme_account.key"
|
const keyName = "acme_account+key"
|
||||||
|
|
||||||
|
// Previous versions of autocert stored the value under a different key.
|
||||||
|
const legacyKeyName = "acme_account.key"
|
||||||
|
|
||||||
genKey := func() (*ecdsa.PrivateKey, error) {
|
genKey := func() (*ecdsa.PrivateKey, error) {
|
||||||
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
@ -602,6 +917,9 @@ func (m *Manager) accountKey(ctx context.Context) (crypto.Signer, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := m.Cache.Get(ctx, keyName)
|
data, err := m.Cache.Get(ctx, keyName)
|
||||||
|
if err == ErrCacheMiss {
|
||||||
|
data, err = m.Cache.Get(ctx, legacyKeyName)
|
||||||
|
}
|
||||||
if err == ErrCacheMiss {
|
if err == ErrCacheMiss {
|
||||||
key, err := genKey()
|
key, err := genKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -673,6 +991,13 @@ func (m *Manager) renewBefore() time.Duration {
|
||||||
return 720 * time.Hour // 30 days
|
return 720 * time.Hour // 30 days
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Manager) now() time.Time {
|
||||||
|
if m.nowFunc != nil {
|
||||||
|
return m.nowFunc()
|
||||||
|
}
|
||||||
|
return time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
// certState is ready when its mutex is unlocked for reading.
|
// certState is ready when its mutex is unlocked for reading.
|
||||||
type certState struct {
|
type certState struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
|
@ -698,12 +1023,12 @@ func (s *certState) tlscert() (*tls.Certificate, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// certRequest creates a certificate request for the given common name cn
|
// certRequest generates a CSR for the given common name cn and optional SANs.
|
||||||
// and optional SANs.
|
func certRequest(key crypto.Signer, cn string, ext []pkix.Extension, san ...string) ([]byte, error) {
|
||||||
func certRequest(key crypto.Signer, cn string, san ...string) ([]byte, error) {
|
|
||||||
req := &x509.CertificateRequest{
|
req := &x509.CertificateRequest{
|
||||||
Subject: pkix.Name{CommonName: cn},
|
Subject: pkix.Name{CommonName: cn},
|
||||||
DNSNames: san,
|
DNSNames: san,
|
||||||
|
ExtraExtensions: ext,
|
||||||
}
|
}
|
||||||
return x509.CreateCertificateRequest(rand.Reader, req, key)
|
return x509.CreateCertificateRequest(rand.Reader, req, key)
|
||||||
}
|
}
|
||||||
|
@ -734,12 +1059,12 @@ func parsePrivateKey(der []byte) (crypto.Signer, error) {
|
||||||
return nil, errors.New("acme/autocert: failed to parse private key")
|
return nil, errors.New("acme/autocert: failed to parse private key")
|
||||||
}
|
}
|
||||||
|
|
||||||
// validCert parses a cert chain provided as der argument and verifies the leaf, der[0],
|
// validCert parses a cert chain provided as der argument and verifies the leaf and der[0]
|
||||||
// corresponds to the private key, as well as the domain match and expiration dates.
|
// correspond to the private key, the domain and key type match, and expiration dates
|
||||||
// It doesn't do any revocation checking.
|
// are valid. It doesn't do any revocation checking.
|
||||||
//
|
//
|
||||||
// The returned value is the verified leaf cert.
|
// The returned value is the verified leaf cert.
|
||||||
func validCert(domain string, der [][]byte, key crypto.Signer) (leaf *x509.Certificate, err error) {
|
func validCert(ck certKey, der [][]byte, key crypto.Signer, now time.Time) (leaf *x509.Certificate, err error) {
|
||||||
// parse public part(s)
|
// parse public part(s)
|
||||||
var n int
|
var n int
|
||||||
for _, b := range der {
|
for _, b := range der {
|
||||||
|
@ -751,22 +1076,21 @@ func validCert(domain string, der [][]byte, key crypto.Signer) (leaf *x509.Certi
|
||||||
n += copy(pub[n:], b)
|
n += copy(pub[n:], b)
|
||||||
}
|
}
|
||||||
x509Cert, err := x509.ParseCertificates(pub)
|
x509Cert, err := x509.ParseCertificates(pub)
|
||||||
if len(x509Cert) == 0 {
|
if err != nil || len(x509Cert) == 0 {
|
||||||
return nil, errors.New("acme/autocert: no public key found")
|
return nil, errors.New("acme/autocert: no public key found")
|
||||||
}
|
}
|
||||||
// verify the leaf is not expired and matches the domain name
|
// verify the leaf is not expired and matches the domain name
|
||||||
leaf = x509Cert[0]
|
leaf = x509Cert[0]
|
||||||
now := timeNow()
|
|
||||||
if now.Before(leaf.NotBefore) {
|
if now.Before(leaf.NotBefore) {
|
||||||
return nil, errors.New("acme/autocert: certificate is not valid yet")
|
return nil, errors.New("acme/autocert: certificate is not valid yet")
|
||||||
}
|
}
|
||||||
if now.After(leaf.NotAfter) {
|
if now.After(leaf.NotAfter) {
|
||||||
return nil, errors.New("acme/autocert: expired certificate")
|
return nil, errors.New("acme/autocert: expired certificate")
|
||||||
}
|
}
|
||||||
if err := leaf.VerifyHostname(domain); err != nil {
|
if err := leaf.VerifyHostname(ck.domain); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// ensure the leaf corresponds to the private key
|
// ensure the leaf corresponds to the private key and matches the certKey type
|
||||||
switch pub := leaf.PublicKey.(type) {
|
switch pub := leaf.PublicKey.(type) {
|
||||||
case *rsa.PublicKey:
|
case *rsa.PublicKey:
|
||||||
prv, ok := key.(*rsa.PrivateKey)
|
prv, ok := key.(*rsa.PrivateKey)
|
||||||
|
@ -776,6 +1100,9 @@ func validCert(domain string, der [][]byte, key crypto.Signer) (leaf *x509.Certi
|
||||||
if pub.N.Cmp(prv.N) != 0 {
|
if pub.N.Cmp(prv.N) != 0 {
|
||||||
return nil, errors.New("acme/autocert: private key does not match public key")
|
return nil, errors.New("acme/autocert: private key does not match public key")
|
||||||
}
|
}
|
||||||
|
if !ck.isRSA && !ck.isToken {
|
||||||
|
return nil, errors.New("acme/autocert: key type does not match expected value")
|
||||||
|
}
|
||||||
case *ecdsa.PublicKey:
|
case *ecdsa.PublicKey:
|
||||||
prv, ok := key.(*ecdsa.PrivateKey)
|
prv, ok := key.(*ecdsa.PrivateKey)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -784,22 +1111,15 @@ func validCert(domain string, der [][]byte, key crypto.Signer) (leaf *x509.Certi
|
||||||
if pub.X.Cmp(prv.X) != 0 || pub.Y.Cmp(prv.Y) != 0 {
|
if pub.X.Cmp(prv.X) != 0 || pub.Y.Cmp(prv.Y) != 0 {
|
||||||
return nil, errors.New("acme/autocert: private key does not match public key")
|
return nil, errors.New("acme/autocert: private key does not match public key")
|
||||||
}
|
}
|
||||||
|
if ck.isRSA && !ck.isToken {
|
||||||
|
return nil, errors.New("acme/autocert: key type does not match expected value")
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("acme/autocert: unknown public key algorithm")
|
return nil, errors.New("acme/autocert: unknown public key algorithm")
|
||||||
}
|
}
|
||||||
return leaf, nil
|
return leaf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func retryAfter(v string) time.Duration {
|
|
||||||
if i, err := strconv.Atoi(v); err == nil {
|
|
||||||
return time.Duration(i) * time.Second
|
|
||||||
}
|
|
||||||
if t, err := http.ParseTime(v); err == nil {
|
|
||||||
return t.Sub(timeNow())
|
|
||||||
}
|
|
||||||
return time.Second
|
|
||||||
}
|
|
||||||
|
|
||||||
type lockedMathRand struct {
|
type lockedMathRand struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
rnd *mathrand.Rand
|
rnd *mathrand.Rand
|
||||||
|
@ -814,8 +1134,6 @@ func (r *lockedMathRand) int63n(max int64) int64 {
|
||||||
|
|
||||||
// For easier testing.
|
// For easier testing.
|
||||||
var (
|
var (
|
||||||
timeNow = time.Now
|
|
||||||
|
|
||||||
// Called when a state is removed.
|
// Called when a state is removed.
|
||||||
testDidRemoveState = func(domain string) {}
|
testDidRemoveState = func(certKey) {}
|
||||||
)
|
)
|
||||||
|
|
6
vendor/golang.org/x/crypto/acme/autocert/cache.go
generated
vendored
6
vendor/golang.org/x/crypto/acme/autocert/cache.go
generated
vendored
|
@ -16,10 +16,10 @@ import (
|
||||||
var ErrCacheMiss = errors.New("acme/autocert: certificate cache miss")
|
var ErrCacheMiss = errors.New("acme/autocert: certificate cache miss")
|
||||||
|
|
||||||
// Cache is used by Manager to store and retrieve previously obtained certificates
|
// Cache is used by Manager to store and retrieve previously obtained certificates
|
||||||
// as opaque data.
|
// and other account data as opaque blobs.
|
||||||
//
|
//
|
||||||
// The key argument of the methods refers to a domain name but need not be an FQDN.
|
// Cache implementations should not rely on the key naming pattern. Keys can
|
||||||
// Cache implementations should not rely on the key naming pattern.
|
// include any printable ASCII characters, except the following: \/:*?"<>|
|
||||||
type Cache interface {
|
type Cache interface {
|
||||||
// Get returns a certificate data for the specified key.
|
// Get returns a certificate data for the specified key.
|
||||||
// If there's no such key, Get returns ErrCacheMiss.
|
// If there's no such key, Get returns ErrCacheMiss.
|
||||||
|
|
5
vendor/golang.org/x/crypto/acme/autocert/listener.go
generated
vendored
5
vendor/golang.org/x/crypto/acme/autocert/listener.go
generated
vendored
|
@ -73,10 +73,7 @@ func NewListener(domains ...string) net.Listener {
|
||||||
func (m *Manager) Listener() net.Listener {
|
func (m *Manager) Listener() net.Listener {
|
||||||
ln := &listener{
|
ln := &listener{
|
||||||
m: m,
|
m: m,
|
||||||
conf: &tls.Config{
|
conf: m.TLSConfig(),
|
||||||
GetCertificate: m.GetCertificate, // bonus: panic on nil m
|
|
||||||
NextProtos: []string{"h2", "http/1.1"}, // Enable HTTP/2
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
ln.tcpListener, ln.tcpListenErr = net.Listen("tcp", ":443")
|
ln.tcpListener, ln.tcpListenErr = net.Listen("tcp", ":443")
|
||||||
return ln
|
return ln
|
||||||
|
|
39
vendor/golang.org/x/crypto/acme/autocert/renewal.go
generated
vendored
39
vendor/golang.org/x/crypto/acme/autocert/renewal.go
generated
vendored
|
@ -18,7 +18,7 @@ const renewJitter = time.Hour
|
||||||
// renewing a single domain's cert.
|
// renewing a single domain's cert.
|
||||||
type domainRenewal struct {
|
type domainRenewal struct {
|
||||||
m *Manager
|
m *Manager
|
||||||
domain string
|
ck certKey
|
||||||
key crypto.Signer
|
key crypto.Signer
|
||||||
|
|
||||||
timerMu sync.Mutex
|
timerMu sync.Mutex
|
||||||
|
@ -71,25 +71,43 @@ func (dr *domainRenewal) renew() {
|
||||||
testDidRenewLoop(next, err)
|
testDidRenewLoop(next, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updateState locks and replaces the relevant Manager.state item with the given
|
||||||
|
// state. It additionally updates dr.key with the given state's key.
|
||||||
|
func (dr *domainRenewal) updateState(state *certState) {
|
||||||
|
dr.m.stateMu.Lock()
|
||||||
|
defer dr.m.stateMu.Unlock()
|
||||||
|
dr.key = state.key
|
||||||
|
dr.m.state[dr.ck] = state
|
||||||
|
}
|
||||||
|
|
||||||
// do is similar to Manager.createCert but it doesn't lock a Manager.state item.
|
// do is similar to Manager.createCert but it doesn't lock a Manager.state item.
|
||||||
// Instead, it requests a new certificate independently and, upon success,
|
// Instead, it requests a new certificate independently and, upon success,
|
||||||
// replaces dr.m.state item with a new one and updates cache for the given domain.
|
// replaces dr.m.state item with a new one and updates cache for the given domain.
|
||||||
//
|
//
|
||||||
// It may return immediately if the expiration date of the currently cached cert
|
// It may lock and update the Manager.state if the expiration date of the currently
|
||||||
// is far enough in the future.
|
// cached cert is far enough in the future.
|
||||||
//
|
//
|
||||||
// The returned value is a time interval after which the renewal should occur again.
|
// The returned value is a time interval after which the renewal should occur again.
|
||||||
func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
|
func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
|
||||||
// a race is likely unavoidable in a distributed environment
|
// a race is likely unavoidable in a distributed environment
|
||||||
// but we try nonetheless
|
// but we try nonetheless
|
||||||
if tlscert, err := dr.m.cacheGet(ctx, dr.domain); err == nil {
|
if tlscert, err := dr.m.cacheGet(ctx, dr.ck); err == nil {
|
||||||
next := dr.next(tlscert.Leaf.NotAfter)
|
next := dr.next(tlscert.Leaf.NotAfter)
|
||||||
if next > dr.m.renewBefore()+renewJitter {
|
if next > dr.m.renewBefore()+renewJitter {
|
||||||
|
signer, ok := tlscert.PrivateKey.(crypto.Signer)
|
||||||
|
if ok {
|
||||||
|
state := &certState{
|
||||||
|
key: signer,
|
||||||
|
cert: tlscert.Certificate,
|
||||||
|
leaf: tlscert.Leaf,
|
||||||
|
}
|
||||||
|
dr.updateState(state)
|
||||||
return next, nil
|
return next, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
der, leaf, err := dr.m.authorizedCert(ctx, dr.key, dr.domain)
|
der, leaf, err := dr.m.authorizedCert(ctx, dr.key, dr.ck)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -102,16 +120,15 @@ func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
dr.m.cachePut(ctx, dr.domain, tlscert)
|
if err := dr.m.cachePut(ctx, dr.ck, tlscert); err != nil {
|
||||||
dr.m.stateMu.Lock()
|
return 0, err
|
||||||
defer dr.m.stateMu.Unlock()
|
}
|
||||||
// m.state is guaranteed to be non-nil at this point
|
dr.updateState(state)
|
||||||
dr.m.state[dr.domain] = state
|
|
||||||
return dr.next(leaf.NotAfter), nil
|
return dr.next(leaf.NotAfter), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dr *domainRenewal) next(expiry time.Time) time.Duration {
|
func (dr *domainRenewal) next(expiry time.Time) time.Duration {
|
||||||
d := expiry.Sub(timeNow()) - dr.m.renewBefore()
|
d := expiry.Sub(dr.m.now()) - dr.m.renewBefore()
|
||||||
// add a bit of randomness to renew deadline
|
// add a bit of randomness to renew deadline
|
||||||
n := pseudoRand.int63n(int64(renewJitter))
|
n := pseudoRand.int63n(int64(renewJitter))
|
||||||
d -= time.Duration(n)
|
d -= time.Duration(n)
|
||||||
|
|
281
vendor/golang.org/x/crypto/acme/http.go
generated
vendored
Normal file
281
vendor/golang.org/x/crypto/acme/http.go
generated
vendored
Normal file
|
@ -0,0 +1,281 @@
|
||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/big"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// retryTimer encapsulates common logic for retrying unsuccessful requests.
|
||||||
|
// It is not safe for concurrent use.
|
||||||
|
type retryTimer struct {
|
||||||
|
// backoffFn provides backoff delay sequence for retries.
|
||||||
|
// See Client.RetryBackoff doc comment.
|
||||||
|
backoffFn func(n int, r *http.Request, res *http.Response) time.Duration
|
||||||
|
// n is the current retry attempt.
|
||||||
|
n int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *retryTimer) inc() {
|
||||||
|
t.n++
|
||||||
|
}
|
||||||
|
|
||||||
|
// backoff pauses the current goroutine as described in Client.RetryBackoff.
|
||||||
|
func (t *retryTimer) backoff(ctx context.Context, r *http.Request, res *http.Response) error {
|
||||||
|
d := t.backoffFn(t.n, r, res)
|
||||||
|
if d <= 0 {
|
||||||
|
return fmt.Errorf("acme: no more retries for %s; tried %d time(s)", r.URL, t.n)
|
||||||
|
}
|
||||||
|
wakeup := time.NewTimer(d)
|
||||||
|
defer wakeup.Stop()
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case <-wakeup.C:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) retryTimer() *retryTimer {
|
||||||
|
f := c.RetryBackoff
|
||||||
|
if f == nil {
|
||||||
|
f = defaultBackoff
|
||||||
|
}
|
||||||
|
return &retryTimer{backoffFn: f}
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultBackoff provides default Client.RetryBackoff implementation
|
||||||
|
// using a truncated exponential backoff algorithm,
|
||||||
|
// as described in Client.RetryBackoff.
|
||||||
|
//
|
||||||
|
// The n argument is always bounded between 1 and 30.
|
||||||
|
// The returned value is always greater than 0.
|
||||||
|
func defaultBackoff(n int, r *http.Request, res *http.Response) time.Duration {
|
||||||
|
const max = 10 * time.Second
|
||||||
|
var jitter time.Duration
|
||||||
|
if x, err := rand.Int(rand.Reader, big.NewInt(1000)); err == nil {
|
||||||
|
// Set the minimum to 1ms to avoid a case where
|
||||||
|
// an invalid Retry-After value is parsed into 0 below,
|
||||||
|
// resulting in the 0 returned value which would unintentionally
|
||||||
|
// stop the retries.
|
||||||
|
jitter = (1 + time.Duration(x.Int64())) * time.Millisecond
|
||||||
|
}
|
||||||
|
if v, ok := res.Header["Retry-After"]; ok {
|
||||||
|
return retryAfter(v[0]) + jitter
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < 1 {
|
||||||
|
n = 1
|
||||||
|
}
|
||||||
|
if n > 30 {
|
||||||
|
n = 30
|
||||||
|
}
|
||||||
|
d := time.Duration(1<<uint(n-1))*time.Second + jitter
|
||||||
|
if d > max {
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// retryAfter parses a Retry-After HTTP header value,
|
||||||
|
// trying to convert v into an int (seconds) or use http.ParseTime otherwise.
|
||||||
|
// It returns zero value if v cannot be parsed.
|
||||||
|
func retryAfter(v string) time.Duration {
|
||||||
|
if i, err := strconv.Atoi(v); err == nil {
|
||||||
|
return time.Duration(i) * time.Second
|
||||||
|
}
|
||||||
|
t, err := http.ParseTime(v)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return t.Sub(timeNow())
|
||||||
|
}
|
||||||
|
|
||||||
|
// resOkay is a function that reports whether the provided response is okay.
|
||||||
|
// It is expected to keep the response body unread.
|
||||||
|
type resOkay func(*http.Response) bool
|
||||||
|
|
||||||
|
// wantStatus returns a function which reports whether the code
|
||||||
|
// matches the status code of a response.
|
||||||
|
func wantStatus(codes ...int) resOkay {
|
||||||
|
return func(res *http.Response) bool {
|
||||||
|
for _, code := range codes {
|
||||||
|
if code == res.StatusCode {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get issues an unsigned GET request to the specified URL.
|
||||||
|
// It returns a non-error value only when ok reports true.
|
||||||
|
//
|
||||||
|
// get retries unsuccessful attempts according to c.RetryBackoff
|
||||||
|
// until the context is done or a non-retriable error is received.
|
||||||
|
func (c *Client) get(ctx context.Context, url string, ok resOkay) (*http.Response, error) {
|
||||||
|
retry := c.retryTimer()
|
||||||
|
for {
|
||||||
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res, err := c.doNoRetry(ctx, req)
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
return nil, err
|
||||||
|
case ok(res):
|
||||||
|
return res, nil
|
||||||
|
case isRetriable(res.StatusCode):
|
||||||
|
retry.inc()
|
||||||
|
resErr := responseError(res)
|
||||||
|
res.Body.Close()
|
||||||
|
// Ignore the error value from retry.backoff
|
||||||
|
// and return the one from last retry, as received from the CA.
|
||||||
|
if retry.backoff(ctx, req, res) != nil {
|
||||||
|
return nil, resErr
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
defer res.Body.Close()
|
||||||
|
return nil, responseError(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// post issues a signed POST request in JWS format using the provided key
|
||||||
|
// to the specified URL.
|
||||||
|
// It returns a non-error value only when ok reports true.
|
||||||
|
//
|
||||||
|
// post retries unsuccessful attempts according to c.RetryBackoff
|
||||||
|
// until the context is done or a non-retriable error is received.
|
||||||
|
// It uses postNoRetry to make individual requests.
|
||||||
|
func (c *Client) post(ctx context.Context, key crypto.Signer, url string, body interface{}, ok resOkay) (*http.Response, error) {
|
||||||
|
retry := c.retryTimer()
|
||||||
|
for {
|
||||||
|
res, req, err := c.postNoRetry(ctx, key, url, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ok(res) {
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
resErr := responseError(res)
|
||||||
|
res.Body.Close()
|
||||||
|
switch {
|
||||||
|
// Check for bad nonce before isRetriable because it may have been returned
|
||||||
|
// with an unretriable response code such as 400 Bad Request.
|
||||||
|
case isBadNonce(resErr):
|
||||||
|
// Consider any previously stored nonce values to be invalid.
|
||||||
|
c.clearNonces()
|
||||||
|
case !isRetriable(res.StatusCode):
|
||||||
|
return nil, resErr
|
||||||
|
}
|
||||||
|
retry.inc()
|
||||||
|
// Ignore the error value from retry.backoff
|
||||||
|
// and return the one from last retry, as received from the CA.
|
||||||
|
if err := retry.backoff(ctx, req, res); err != nil {
|
||||||
|
return nil, resErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// postNoRetry signs the body with the given key and POSTs it to the provided url.
|
||||||
|
// The body argument must be JSON-serializable.
|
||||||
|
// It is used by c.post to retry unsuccessful attempts.
|
||||||
|
func (c *Client) postNoRetry(ctx context.Context, key crypto.Signer, url string, body interface{}) (*http.Response, *http.Request, error) {
|
||||||
|
nonce, err := c.popNonce(ctx, url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
b, err := jwsEncodeJSON(body, key, nonce)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest("POST", url, bytes.NewReader(b))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/jose+json")
|
||||||
|
res, err := c.doNoRetry(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
c.addNonce(res.Header)
|
||||||
|
return res, req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// doNoRetry issues a request req, replacing its context (if any) with ctx.
|
||||||
|
func (c *Client) doNoRetry(ctx context.Context, req *http.Request) (*http.Response, error) {
|
||||||
|
res, err := c.httpClient().Do(req.WithContext(ctx))
|
||||||
|
if err != nil {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
// Prefer the unadorned context error.
|
||||||
|
// (The acme package had tests assuming this, previously from ctxhttp's
|
||||||
|
// behavior, predating net/http supporting contexts natively)
|
||||||
|
// TODO(bradfitz): reconsider this in the future. But for now this
|
||||||
|
// requires no test updates.
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) httpClient() *http.Client {
|
||||||
|
if c.HTTPClient != nil {
|
||||||
|
return c.HTTPClient
|
||||||
|
}
|
||||||
|
return http.DefaultClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// isBadNonce reports whether err is an ACME "badnonce" error.
|
||||||
|
func isBadNonce(err error) bool {
|
||||||
|
// According to the spec badNonce is urn:ietf:params:acme:error:badNonce.
|
||||||
|
// However, ACME servers in the wild return their versions of the error.
|
||||||
|
// See https://tools.ietf.org/html/draft-ietf-acme-acme-02#section-5.4
|
||||||
|
// and https://github.com/letsencrypt/boulder/blob/0e07eacb/docs/acme-divergences.md#section-66.
|
||||||
|
ae, ok := err.(*Error)
|
||||||
|
return ok && strings.HasSuffix(strings.ToLower(ae.ProblemType), ":badnonce")
|
||||||
|
}
|
||||||
|
|
||||||
|
// isRetriable reports whether a request can be retried
|
||||||
|
// based on the response status code.
|
||||||
|
//
|
||||||
|
// Note that a "bad nonce" error is returned with a non-retriable 400 Bad Request code.
|
||||||
|
// Callers should parse the response and check with isBadNonce.
|
||||||
|
func isRetriable(code int) bool {
|
||||||
|
return code <= 399 || code >= 500 || code == http.StatusTooManyRequests
|
||||||
|
}
|
||||||
|
|
||||||
|
// responseError creates an error of Error type from resp.
|
||||||
|
func responseError(resp *http.Response) error {
|
||||||
|
// don't care if ReadAll returns an error:
|
||||||
|
// json.Unmarshal will fail in that case anyway
|
||||||
|
b, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
e := &wireError{Status: resp.StatusCode}
|
||||||
|
if err := json.Unmarshal(b, e); err != nil {
|
||||||
|
// this is not a regular error response:
|
||||||
|
// populate detail with anything we received,
|
||||||
|
// e.Status will already contain HTTP response code value
|
||||||
|
e.Detail = string(b)
|
||||||
|
if e.Detail == "" {
|
||||||
|
e.Detail = resp.Status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e.error(resp.Header)
|
||||||
|
}
|
29
vendor/golang.org/x/crypto/acme/jws.go
generated
vendored
29
vendor/golang.org/x/crypto/acme/jws.go
generated
vendored
|
@ -25,7 +25,7 @@ func jwsEncodeJSON(claimset interface{}, key crypto.Signer, nonce string) ([]byt
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
alg, sha := jwsHasher(key)
|
alg, sha := jwsHasher(key.Public())
|
||||||
if alg == "" || !sha.Available() {
|
if alg == "" || !sha.Available() {
|
||||||
return nil, ErrUnsupportedKey
|
return nil, ErrUnsupportedKey
|
||||||
}
|
}
|
||||||
|
@ -97,13 +97,16 @@ func jwkEncode(pub crypto.PublicKey) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// jwsSign signs the digest using the given key.
|
// jwsSign signs the digest using the given key.
|
||||||
// It returns ErrUnsupportedKey if the key type is unknown.
|
// The hash is unused for ECDSA keys.
|
||||||
// The hash is used only for RSA keys.
|
//
|
||||||
|
// Note: non-stdlib crypto.Signer implementations are expected to return
|
||||||
|
// the signature in the format as specified in RFC7518.
|
||||||
|
// See https://tools.ietf.org/html/rfc7518 for more details.
|
||||||
func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error) {
|
func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error) {
|
||||||
switch key := key.(type) {
|
if key, ok := key.(*ecdsa.PrivateKey); ok {
|
||||||
case *rsa.PrivateKey:
|
// The key.Sign method of ecdsa returns ASN1-encoded signature.
|
||||||
return key.Sign(rand.Reader, digest, hash)
|
// So, we use the package Sign function instead
|
||||||
case *ecdsa.PrivateKey:
|
// to get R and S values directly and format the result accordingly.
|
||||||
r, s, err := ecdsa.Sign(rand.Reader, key, digest)
|
r, s, err := ecdsa.Sign(rand.Reader, key, digest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -118,18 +121,18 @@ func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error)
|
||||||
copy(sig[size*2-len(sb):], sb)
|
copy(sig[size*2-len(sb):], sb)
|
||||||
return sig, nil
|
return sig, nil
|
||||||
}
|
}
|
||||||
return nil, ErrUnsupportedKey
|
return key.Sign(rand.Reader, digest, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// jwsHasher indicates suitable JWS algorithm name and a hash function
|
// jwsHasher indicates suitable JWS algorithm name and a hash function
|
||||||
// to use for signing a digest with the provided key.
|
// to use for signing a digest with the provided key.
|
||||||
// It returns ("", 0) if the key is not supported.
|
// It returns ("", 0) if the key is not supported.
|
||||||
func jwsHasher(key crypto.Signer) (string, crypto.Hash) {
|
func jwsHasher(pub crypto.PublicKey) (string, crypto.Hash) {
|
||||||
switch key := key.(type) {
|
switch pub := pub.(type) {
|
||||||
case *rsa.PrivateKey:
|
case *rsa.PublicKey:
|
||||||
return "RS256", crypto.SHA256
|
return "RS256", crypto.SHA256
|
||||||
case *ecdsa.PrivateKey:
|
case *ecdsa.PublicKey:
|
||||||
switch key.Params().Name {
|
switch pub.Params().Name {
|
||||||
case "P-256":
|
case "P-256":
|
||||||
return "ES256", crypto.SHA256
|
return "ES256", crypto.SHA256
|
||||||
case "P-384":
|
case "P-384":
|
||||||
|
|
8
vendor/golang.org/x/crypto/acme/types.go
generated
vendored
8
vendor/golang.org/x/crypto/acme/types.go
generated
vendored
|
@ -104,7 +104,7 @@ func RateLimit(err error) (time.Duration, bool) {
|
||||||
if e.Header == nil {
|
if e.Header == nil {
|
||||||
return 0, true
|
return 0, true
|
||||||
}
|
}
|
||||||
return retryAfter(e.Header.Get("Retry-After"), 0), true
|
return retryAfter(e.Header.Get("Retry-After")), true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Account is a user account. It is associated with a private key.
|
// Account is a user account. It is associated with a private key.
|
||||||
|
@ -296,8 +296,8 @@ func (e *wireError) error(h http.Header) *Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CertOption is an optional argument type for the TLSSNIxChallengeCert methods for
|
// CertOption is an optional argument type for the TLS ChallengeCert methods for
|
||||||
// customizing a temporary certificate for TLS-SNI challenges.
|
// customizing a temporary certificate for TLS-based challenges.
|
||||||
type CertOption interface {
|
type CertOption interface {
|
||||||
privateCertOpt()
|
privateCertOpt()
|
||||||
}
|
}
|
||||||
|
@ -317,7 +317,7 @@ func (*certOptKey) privateCertOpt() {}
|
||||||
// WithTemplate creates an option for specifying a certificate template.
|
// WithTemplate creates an option for specifying a certificate template.
|
||||||
// See x509.CreateCertificate for template usage details.
|
// See x509.CreateCertificate for template usage details.
|
||||||
//
|
//
|
||||||
// In TLSSNIxChallengeCert methods, the template is also used as parent,
|
// In TLS ChallengeCert methods, the template is also used as parent,
|
||||||
// resulting in a self-signed certificate.
|
// resulting in a self-signed certificate.
|
||||||
// The DNSNames field of t is always overwritten for tls-sni challenge certs.
|
// The DNSNames field of t is always overwritten for tls-sni challenge certs.
|
||||||
func WithTemplate(t *x509.Certificate) CertOption {
|
func WithTemplate(t *x509.Certificate) CertOption {
|
||||||
|
|
4
vendor/golang.org/x/crypto/bcrypt/bcrypt.go
generated
vendored
4
vendor/golang.org/x/crypto/bcrypt/bcrypt.go
generated
vendored
|
@ -241,11 +241,11 @@ func (p *hashed) Hash() []byte {
|
||||||
n = 3
|
n = 3
|
||||||
}
|
}
|
||||||
arr[n] = '$'
|
arr[n] = '$'
|
||||||
n += 1
|
n++
|
||||||
copy(arr[n:], []byte(fmt.Sprintf("%02d", p.cost)))
|
copy(arr[n:], []byte(fmt.Sprintf("%02d", p.cost)))
|
||||||
n += 2
|
n += 2
|
||||||
arr[n] = '$'
|
arr[n] = '$'
|
||||||
n += 1
|
n++
|
||||||
copy(arr[n:], p.salt)
|
copy(arr[n:], p.salt)
|
||||||
n += encodedSaltSize
|
n += encodedSaltSize
|
||||||
copy(arr[n:], p.hash)
|
copy(arr[n:], p.hash)
|
||||||
|
|
8
vendor/golang.org/x/crypto/blowfish/cipher.go
generated
vendored
8
vendor/golang.org/x/crypto/blowfish/cipher.go
generated
vendored
|
@ -3,6 +3,14 @@
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// Package blowfish implements Bruce Schneier's Blowfish encryption algorithm.
|
// Package blowfish implements Bruce Schneier's Blowfish encryption algorithm.
|
||||||
|
//
|
||||||
|
// Blowfish is a legacy cipher and its short block size makes it vulnerable to
|
||||||
|
// birthday bound attacks (see https://sweet32.info). It should only be used
|
||||||
|
// where compatibility with legacy systems, not security, is the goal.
|
||||||
|
//
|
||||||
|
// Deprecated: any new system should use AES (from crypto/aes, if necessary in
|
||||||
|
// an AEAD mode like crypto/cipher.NewGCM) or XChaCha20-Poly1305 (from
|
||||||
|
// golang.org/x/crypto/chacha20poly1305).
|
||||||
package blowfish // import "golang.org/x/crypto/blowfish"
|
package blowfish // import "golang.org/x/crypto/blowfish"
|
||||||
|
|
||||||
// The code is a port of Bruce Schneier's C implementation.
|
// The code is a port of Bruce Schneier's C implementation.
|
||||||
|
|
3
vendor/golang.org/x/net/AUTHORS
generated
vendored
Normal file
3
vendor/golang.org/x/net/AUTHORS
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# This source code refers to The Go Authors for copyright purposes.
|
||||||
|
# The master list of authors is in the main Go distribution,
|
||||||
|
# visible at http://tip.golang.org/AUTHORS.
|
3
vendor/golang.org/x/net/CONTRIBUTORS
generated
vendored
Normal file
3
vendor/golang.org/x/net/CONTRIBUTORS
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# This source code was written by the Go contributors.
|
||||||
|
# The master list of contributors is in the main Go distribution,
|
||||||
|
# visible at http://tip.golang.org/CONTRIBUTORS.
|
3
vendor/golang.org/x/sys/AUTHORS
generated
vendored
Normal file
3
vendor/golang.org/x/sys/AUTHORS
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# This source code refers to The Go Authors for copyright purposes.
|
||||||
|
# The master list of authors is in the main Go distribution,
|
||||||
|
# visible at http://tip.golang.org/AUTHORS.
|
3
vendor/golang.org/x/sys/CONTRIBUTORS
generated
vendored
Normal file
3
vendor/golang.org/x/sys/CONTRIBUTORS
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# This source code was written by the Go contributors.
|
||||||
|
# The master list of contributors is in the main Go distribution,
|
||||||
|
# visible at http://tip.golang.org/CONTRIBUTORS.
|
2
vendor/golang.org/x/sys/unix/.gitignore
generated
vendored
Normal file
2
vendor/golang.org/x/sys/unix/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
_obj/
|
||||||
|
unix.test
|
20
vendor/golang.org/x/sys/unix/README.md
generated
vendored
20
vendor/golang.org/x/sys/unix/README.md
generated
vendored
|
@ -14,7 +14,7 @@ migrating the build system to use containers so the builds are reproducible.
|
||||||
This is being done on an OS-by-OS basis. Please update this documentation as
|
This is being done on an OS-by-OS basis. Please update this documentation as
|
||||||
components of the build system change.
|
components of the build system change.
|
||||||
|
|
||||||
### Old Build System (currently for `GOOS != "Linux" || GOARCH == "sparc64"`)
|
### Old Build System (currently for `GOOS != "linux"`)
|
||||||
|
|
||||||
The old build system generates the Go files based on the C header files
|
The old build system generates the Go files based on the C header files
|
||||||
present on your system. This means that files
|
present on your system. This means that files
|
||||||
|
@ -32,9 +32,9 @@ To build the files for your current OS and architecture, make sure GOOS and
|
||||||
GOARCH are set correctly and run `mkall.sh`. This will generate the files for
|
GOARCH are set correctly and run `mkall.sh`. This will generate the files for
|
||||||
your specific system. Running `mkall.sh -n` shows the commands that will be run.
|
your specific system. Running `mkall.sh -n` shows the commands that will be run.
|
||||||
|
|
||||||
Requirements: bash, perl, go
|
Requirements: bash, go
|
||||||
|
|
||||||
### New Build System (currently for `GOOS == "Linux" && GOARCH != "sparc64"`)
|
### New Build System (currently for `GOOS == "linux"`)
|
||||||
|
|
||||||
The new build system uses a Docker container to generate the go files directly
|
The new build system uses a Docker container to generate the go files directly
|
||||||
from source checkouts of the kernel and various system libraries. This means
|
from source checkouts of the kernel and various system libraries. This means
|
||||||
|
@ -52,14 +52,14 @@ system and have your GOOS and GOARCH set accordingly. Running `mkall.sh` will
|
||||||
then generate all of the files for all of the GOOS/GOARCH pairs in the new build
|
then generate all of the files for all of the GOOS/GOARCH pairs in the new build
|
||||||
system. Running `mkall.sh -n` shows the commands that will be run.
|
system. Running `mkall.sh -n` shows the commands that will be run.
|
||||||
|
|
||||||
Requirements: bash, perl, go, docker
|
Requirements: bash, go, docker
|
||||||
|
|
||||||
## Component files
|
## Component files
|
||||||
|
|
||||||
This section describes the various files used in the code generation process.
|
This section describes the various files used in the code generation process.
|
||||||
It also contains instructions on how to modify these files to add a new
|
It also contains instructions on how to modify these files to add a new
|
||||||
architecture/OS or to add additional syscalls, types, or constants. Note that
|
architecture/OS or to add additional syscalls, types, or constants. Note that
|
||||||
if you are using the new build system, the scripts cannot be called normally.
|
if you are using the new build system, the scripts/programs cannot be called normally.
|
||||||
They must be called from within the docker container.
|
They must be called from within the docker container.
|
||||||
|
|
||||||
### asm files
|
### asm files
|
||||||
|
@ -81,8 +81,8 @@ each GOOS/GOARCH pair.
|
||||||
|
|
||||||
### mksysnum
|
### mksysnum
|
||||||
|
|
||||||
Mksysnum is a script located at `${GOOS}/mksysnum.pl` (or `mksysnum_${GOOS}.pl`
|
Mksysnum is a Go program located at `${GOOS}/mksysnum.go` (or `mksysnum_${GOOS}.go`
|
||||||
for the old system). This script takes in a list of header files containing the
|
for the old system). This program takes in a list of header files containing the
|
||||||
syscall number declarations and parses them to produce the corresponding list of
|
syscall number declarations and parses them to produce the corresponding list of
|
||||||
Go numeric constants. See `zsysnum_${GOOS}_${GOARCH}.go` for the generated
|
Go numeric constants. See `zsysnum_${GOOS}_${GOARCH}.go` for the generated
|
||||||
constants.
|
constants.
|
||||||
|
@ -92,14 +92,14 @@ new installation of the target OS (or updating the source checkouts for the
|
||||||
new build system). However, depending on the OS, you make need to update the
|
new build system). However, depending on the OS, you make need to update the
|
||||||
parsing in mksysnum.
|
parsing in mksysnum.
|
||||||
|
|
||||||
### mksyscall.pl
|
### mksyscall.go
|
||||||
|
|
||||||
The `syscall.go`, `syscall_${GOOS}.go`, `syscall_${GOOS}_${GOARCH}.go` are
|
The `syscall.go`, `syscall_${GOOS}.go`, `syscall_${GOOS}_${GOARCH}.go` are
|
||||||
hand-written Go files which implement system calls (for unix, the specific OS,
|
hand-written Go files which implement system calls (for unix, the specific OS,
|
||||||
or the specific OS/Architecture pair respectively) that need special handling
|
or the specific OS/Architecture pair respectively) that need special handling
|
||||||
and list `//sys` comments giving prototypes for ones that can be generated.
|
and list `//sys` comments giving prototypes for ones that can be generated.
|
||||||
|
|
||||||
The mksyscall.pl script takes the `//sys` and `//sysnb` comments and converts
|
The mksyscall.go program takes the `//sys` and `//sysnb` comments and converts
|
||||||
them into syscalls. This requires the name of the prototype in the comment to
|
them into syscalls. This requires the name of the prototype in the comment to
|
||||||
match a syscall number in the `zsysnum_${GOOS}_${GOARCH}.go` file. The function
|
match a syscall number in the `zsysnum_${GOOS}_${GOARCH}.go` file. The function
|
||||||
prototype can be exported (capitalized) or not.
|
prototype can be exported (capitalized) or not.
|
||||||
|
@ -160,7 +160,7 @@ signal numbers, and constants. Generated by `mkerrors.sh` (see above).
|
||||||
### `zsyscall_${GOOS}_${GOARCH}.go`
|
### `zsyscall_${GOOS}_${GOARCH}.go`
|
||||||
|
|
||||||
A file containing all the generated syscalls for a specific GOOS and GOARCH.
|
A file containing all the generated syscalls for a specific GOOS and GOARCH.
|
||||||
Generated by `mksyscall.pl` (see above).
|
Generated by `mksyscall.go` (see above).
|
||||||
|
|
||||||
### `zsysnum_${GOOS}_${GOARCH}.go`
|
### `zsysnum_${GOOS}_${GOARCH}.go`
|
||||||
|
|
||||||
|
|
124
vendor/golang.org/x/sys/unix/affinity_linux.go
generated
vendored
Normal file
124
vendor/golang.org/x/sys/unix/affinity_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
// Copyright 2018 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.
|
||||||
|
|
||||||
|
// CPU affinity functions
|
||||||
|
|
||||||
|
package unix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const cpuSetSize = _CPU_SETSIZE / _NCPUBITS
|
||||||
|
|
||||||
|
// CPUSet represents a CPU affinity mask.
|
||||||
|
type CPUSet [cpuSetSize]cpuMask
|
||||||
|
|
||||||
|
func schedAffinity(trap uintptr, pid int, set *CPUSet) error {
|
||||||
|
_, _, e := RawSyscall(trap, uintptr(pid), uintptr(unsafe.Sizeof(*set)), uintptr(unsafe.Pointer(set)))
|
||||||
|
if e != 0 {
|
||||||
|
return errnoErr(e)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SchedGetaffinity gets the CPU affinity mask of the thread specified by pid.
|
||||||
|
// If pid is 0 the calling thread is used.
|
||||||
|
func SchedGetaffinity(pid int, set *CPUSet) error {
|
||||||
|
return schedAffinity(SYS_SCHED_GETAFFINITY, pid, set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SchedSetaffinity sets the CPU affinity mask of the thread specified by pid.
|
||||||
|
// If pid is 0 the calling thread is used.
|
||||||
|
func SchedSetaffinity(pid int, set *CPUSet) error {
|
||||||
|
return schedAffinity(SYS_SCHED_SETAFFINITY, pid, set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zero clears the set s, so that it contains no CPUs.
|
||||||
|
func (s *CPUSet) Zero() {
|
||||||
|
for i := range s {
|
||||||
|
s[i] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cpuBitsIndex(cpu int) int {
|
||||||
|
return cpu / _NCPUBITS
|
||||||
|
}
|
||||||
|
|
||||||
|
func cpuBitsMask(cpu int) cpuMask {
|
||||||
|
return cpuMask(1 << (uint(cpu) % _NCPUBITS))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set adds cpu to the set s.
|
||||||
|
func (s *CPUSet) Set(cpu int) {
|
||||||
|
i := cpuBitsIndex(cpu)
|
||||||
|
if i < len(s) {
|
||||||
|
s[i] |= cpuBitsMask(cpu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear removes cpu from the set s.
|
||||||
|
func (s *CPUSet) Clear(cpu int) {
|
||||||
|
i := cpuBitsIndex(cpu)
|
||||||
|
if i < len(s) {
|
||||||
|
s[i] &^= cpuBitsMask(cpu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSet reports whether cpu is in the set s.
|
||||||
|
func (s *CPUSet) IsSet(cpu int) bool {
|
||||||
|
i := cpuBitsIndex(cpu)
|
||||||
|
if i < len(s) {
|
||||||
|
return s[i]&cpuBitsMask(cpu) != 0
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count returns the number of CPUs in the set s.
|
||||||
|
func (s *CPUSet) Count() int {
|
||||||
|
c := 0
|
||||||
|
for _, b := range s {
|
||||||
|
c += onesCount64(uint64(b))
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// onesCount64 is a copy of Go 1.9's math/bits.OnesCount64.
|
||||||
|
// Once this package can require Go 1.9, we can delete this
|
||||||
|
// and update the caller to use bits.OnesCount64.
|
||||||
|
func onesCount64(x uint64) int {
|
||||||
|
const m0 = 0x5555555555555555 // 01010101 ...
|
||||||
|
const m1 = 0x3333333333333333 // 00110011 ...
|
||||||
|
const m2 = 0x0f0f0f0f0f0f0f0f // 00001111 ...
|
||||||
|
const m3 = 0x00ff00ff00ff00ff // etc.
|
||||||
|
const m4 = 0x0000ffff0000ffff
|
||||||
|
|
||||||
|
// Implementation: Parallel summing of adjacent bits.
|
||||||
|
// See "Hacker's Delight", Chap. 5: Counting Bits.
|
||||||
|
// The following pattern shows the general approach:
|
||||||
|
//
|
||||||
|
// x = x>>1&(m0&m) + x&(m0&m)
|
||||||
|
// x = x>>2&(m1&m) + x&(m1&m)
|
||||||
|
// x = x>>4&(m2&m) + x&(m2&m)
|
||||||
|
// x = x>>8&(m3&m) + x&(m3&m)
|
||||||
|
// x = x>>16&(m4&m) + x&(m4&m)
|
||||||
|
// x = x>>32&(m5&m) + x&(m5&m)
|
||||||
|
// return int(x)
|
||||||
|
//
|
||||||
|
// Masking (& operations) can be left away when there's no
|
||||||
|
// danger that a field's sum will carry over into the next
|
||||||
|
// field: Since the result cannot be > 64, 8 bits is enough
|
||||||
|
// and we can ignore the masks for the shifts by 8 and up.
|
||||||
|
// Per "Hacker's Delight", the first line can be simplified
|
||||||
|
// more, but it saves at best one instruction, so we leave
|
||||||
|
// it alone for clarity.
|
||||||
|
const m = 1<<64 - 1
|
||||||
|
x = x>>1&(m0&m) + x&(m0&m)
|
||||||
|
x = x>>2&(m1&m) + x&(m1&m)
|
||||||
|
x = (x>>4 + x) & (m2 & m)
|
||||||
|
x += x >> 8
|
||||||
|
x += x >> 16
|
||||||
|
x += x >> 32
|
||||||
|
return int(x) & (1<<7 - 1)
|
||||||
|
}
|
14
vendor/golang.org/x/sys/unix/aliases.go
generated
vendored
Normal file
14
vendor/golang.org/x/sys/unix/aliases.go
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// Copyright 2018 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 aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||||
|
// +build go1.9
|
||||||
|
|
||||||
|
package unix
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
type Signal = syscall.Signal
|
||||||
|
type Errno = syscall.Errno
|
||||||
|
type SysProcAttr = syscall.SysProcAttr
|
17
vendor/golang.org/x/sys/unix/asm_aix_ppc64.s
generated
vendored
Normal file
17
vendor/golang.org/x/sys/unix/asm_aix_ppc64.s
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright 2018 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 !gccgo
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// System calls for ppc64, AIX are implemented in runtime/syscall_aix.go
|
||||||
|
//
|
||||||
|
|
||||||
|
TEXT ·syscall6(SB),NOSPLIT,$0-88
|
||||||
|
JMP syscall·syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·rawSyscall6(SB),NOSPLIT,$0-88
|
||||||
|
JMP syscall·rawSyscall6(SB)
|
10
vendor/golang.org/x/sys/unix/asm_dragonfly_amd64.s
generated
vendored
10
vendor/golang.org/x/sys/unix/asm_dragonfly_amd64.s
generated
vendored
|
@ -13,17 +13,17 @@
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
// The runtime may know about them.
|
// The runtime may know about them.
|
||||||
|
|
||||||
TEXT ·Syscall(SB),NOSPLIT,$0-64
|
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||||
JMP syscall·Syscall(SB)
|
JMP syscall·Syscall(SB)
|
||||||
|
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-88
|
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||||
JMP syscall·Syscall6(SB)
|
JMP syscall·Syscall6(SB)
|
||||||
|
|
||||||
TEXT ·Syscall9(SB),NOSPLIT,$0-112
|
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||||
JMP syscall·Syscall9(SB)
|
JMP syscall·Syscall9(SB)
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-64
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||||
JMP syscall·RawSyscall(SB)
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-88
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||||
JMP syscall·RawSyscall6(SB)
|
JMP syscall·RawSyscall6(SB)
|
||||||
|
|
29
vendor/golang.org/x/sys/unix/asm_freebsd_arm64.s
generated
vendored
Normal file
29
vendor/golang.org/x/sys/unix/asm_freebsd_arm64.s
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright 2018 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 !gccgo
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// System call support for ARM64, FreeBSD
|
||||||
|
//
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||||
|
JMP syscall·Syscall9(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·RawSyscall6(SB)
|
30
vendor/golang.org/x/sys/unix/asm_linux_386.s
generated
vendored
30
vendor/golang.org/x/sys/unix/asm_linux_386.s
generated
vendored
|
@ -10,6 +10,10 @@
|
||||||
// System calls for 386, Linux
|
// System calls for 386, Linux
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// See ../runtime/sys_linux_386.s for the reason why we always use int 0x80
|
||||||
|
// instead of the glibc-specific "CALL 0x10(GS)".
|
||||||
|
#define INVOKE_SYSCALL INT $0x80
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
// The runtime may know about them.
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
@ -19,12 +23,38 @@ TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||||
JMP syscall·Syscall6(SB)
|
JMP syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·SyscallNoError(SB),NOSPLIT,$0-24
|
||||||
|
CALL runtime·entersyscall(SB)
|
||||||
|
MOVL trap+0(FP), AX // syscall entry
|
||||||
|
MOVL a1+4(FP), BX
|
||||||
|
MOVL a2+8(FP), CX
|
||||||
|
MOVL a3+12(FP), DX
|
||||||
|
MOVL $0, SI
|
||||||
|
MOVL $0, DI
|
||||||
|
INVOKE_SYSCALL
|
||||||
|
MOVL AX, r1+16(FP)
|
||||||
|
MOVL DX, r2+20(FP)
|
||||||
|
CALL runtime·exitsyscall(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||||
JMP syscall·RawSyscall(SB)
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||||
JMP syscall·RawSyscall6(SB)
|
JMP syscall·RawSyscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-24
|
||||||
|
MOVL trap+0(FP), AX // syscall entry
|
||||||
|
MOVL a1+4(FP), BX
|
||||||
|
MOVL a2+8(FP), CX
|
||||||
|
MOVL a3+12(FP), DX
|
||||||
|
MOVL $0, SI
|
||||||
|
MOVL $0, DI
|
||||||
|
INVOKE_SYSCALL
|
||||||
|
MOVL AX, r1+16(FP)
|
||||||
|
MOVL DX, r2+20(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
TEXT ·socketcall(SB),NOSPLIT,$0-36
|
TEXT ·socketcall(SB),NOSPLIT,$0-36
|
||||||
JMP syscall·socketcall(SB)
|
JMP syscall·socketcall(SB)
|
||||||
|
|
||||||
|
|
28
vendor/golang.org/x/sys/unix/asm_linux_amd64.s
generated
vendored
28
vendor/golang.org/x/sys/unix/asm_linux_amd64.s
generated
vendored
|
@ -19,11 +19,39 @@ TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||||
JMP syscall·Syscall6(SB)
|
JMP syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||||
|
CALL runtime·entersyscall(SB)
|
||||||
|
MOVQ a1+8(FP), DI
|
||||||
|
MOVQ a2+16(FP), SI
|
||||||
|
MOVQ a3+24(FP), DX
|
||||||
|
MOVQ $0, R10
|
||||||
|
MOVQ $0, R8
|
||||||
|
MOVQ $0, R9
|
||||||
|
MOVQ trap+0(FP), AX // syscall entry
|
||||||
|
SYSCALL
|
||||||
|
MOVQ AX, r1+32(FP)
|
||||||
|
MOVQ DX, r2+40(FP)
|
||||||
|
CALL runtime·exitsyscall(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||||
JMP syscall·RawSyscall(SB)
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||||
JMP syscall·RawSyscall6(SB)
|
JMP syscall·RawSyscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||||
|
MOVQ a1+8(FP), DI
|
||||||
|
MOVQ a2+16(FP), SI
|
||||||
|
MOVQ a3+24(FP), DX
|
||||||
|
MOVQ $0, R10
|
||||||
|
MOVQ $0, R8
|
||||||
|
MOVQ $0, R9
|
||||||
|
MOVQ trap+0(FP), AX // syscall entry
|
||||||
|
SYSCALL
|
||||||
|
MOVQ AX, r1+32(FP)
|
||||||
|
MOVQ DX, r2+40(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
TEXT ·gettimeofday(SB),NOSPLIT,$0-16
|
TEXT ·gettimeofday(SB),NOSPLIT,$0-16
|
||||||
JMP syscall·gettimeofday(SB)
|
JMP syscall·gettimeofday(SB)
|
||||||
|
|
29
vendor/golang.org/x/sys/unix/asm_linux_arm.s
generated
vendored
29
vendor/golang.org/x/sys/unix/asm_linux_arm.s
generated
vendored
|
@ -19,11 +19,38 @@ TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||||
B syscall·Syscall6(SB)
|
B syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·SyscallNoError(SB),NOSPLIT,$0-24
|
||||||
|
BL runtime·entersyscall(SB)
|
||||||
|
MOVW trap+0(FP), R7
|
||||||
|
MOVW a1+4(FP), R0
|
||||||
|
MOVW a2+8(FP), R1
|
||||||
|
MOVW a3+12(FP), R2
|
||||||
|
MOVW $0, R3
|
||||||
|
MOVW $0, R4
|
||||||
|
MOVW $0, R5
|
||||||
|
SWI $0
|
||||||
|
MOVW R0, r1+16(FP)
|
||||||
|
MOVW $0, R0
|
||||||
|
MOVW R0, r2+20(FP)
|
||||||
|
BL runtime·exitsyscall(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||||
B syscall·RawSyscall(SB)
|
B syscall·RawSyscall(SB)
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||||
B syscall·RawSyscall6(SB)
|
B syscall·RawSyscall6(SB)
|
||||||
|
|
||||||
TEXT ·seek(SB),NOSPLIT,$0-32
|
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-24
|
||||||
|
MOVW trap+0(FP), R7 // syscall entry
|
||||||
|
MOVW a1+4(FP), R0
|
||||||
|
MOVW a2+8(FP), R1
|
||||||
|
MOVW a3+12(FP), R2
|
||||||
|
SWI $0
|
||||||
|
MOVW R0, r1+16(FP)
|
||||||
|
MOVW $0, R0
|
||||||
|
MOVW R0, r2+20(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·seek(SB),NOSPLIT,$0-28
|
||||||
B syscall·seek(SB)
|
B syscall·seek(SB)
|
||||||
|
|
28
vendor/golang.org/x/sys/unix/asm_linux_arm64.s
generated
vendored
28
vendor/golang.org/x/sys/unix/asm_linux_arm64.s
generated
vendored
|
@ -17,8 +17,36 @@ TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||||
B syscall·Syscall6(SB)
|
B syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||||
|
BL runtime·entersyscall(SB)
|
||||||
|
MOVD a1+8(FP), R0
|
||||||
|
MOVD a2+16(FP), R1
|
||||||
|
MOVD a3+24(FP), R2
|
||||||
|
MOVD $0, R3
|
||||||
|
MOVD $0, R4
|
||||||
|
MOVD $0, R5
|
||||||
|
MOVD trap+0(FP), R8 // syscall entry
|
||||||
|
SVC
|
||||||
|
MOVD R0, r1+32(FP) // r1
|
||||||
|
MOVD R1, r2+40(FP) // r2
|
||||||
|
BL runtime·exitsyscall(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||||
B syscall·RawSyscall(SB)
|
B syscall·RawSyscall(SB)
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||||
B syscall·RawSyscall6(SB)
|
B syscall·RawSyscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||||
|
MOVD a1+8(FP), R0
|
||||||
|
MOVD a2+16(FP), R1
|
||||||
|
MOVD a3+24(FP), R2
|
||||||
|
MOVD $0, R3
|
||||||
|
MOVD $0, R4
|
||||||
|
MOVD $0, R5
|
||||||
|
MOVD trap+0(FP), R8 // syscall entry
|
||||||
|
SVC
|
||||||
|
MOVD R0, r1+32(FP)
|
||||||
|
MOVD R1, r2+40(FP)
|
||||||
|
RET
|
||||||
|
|
28
vendor/golang.org/x/sys/unix/asm_linux_mips64x.s
generated
vendored
28
vendor/golang.org/x/sys/unix/asm_linux_mips64x.s
generated
vendored
|
@ -21,8 +21,36 @@ TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||||
JMP syscall·Syscall6(SB)
|
JMP syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||||
|
JAL runtime·entersyscall(SB)
|
||||||
|
MOVV a1+8(FP), R4
|
||||||
|
MOVV a2+16(FP), R5
|
||||||
|
MOVV a3+24(FP), R6
|
||||||
|
MOVV R0, R7
|
||||||
|
MOVV R0, R8
|
||||||
|
MOVV R0, R9
|
||||||
|
MOVV trap+0(FP), R2 // syscall entry
|
||||||
|
SYSCALL
|
||||||
|
MOVV R2, r1+32(FP)
|
||||||
|
MOVV R3, r2+40(FP)
|
||||||
|
JAL runtime·exitsyscall(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||||
JMP syscall·RawSyscall(SB)
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||||
JMP syscall·RawSyscall6(SB)
|
JMP syscall·RawSyscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||||
|
MOVV a1+8(FP), R4
|
||||||
|
MOVV a2+16(FP), R5
|
||||||
|
MOVV a3+24(FP), R6
|
||||||
|
MOVV R0, R7
|
||||||
|
MOVV R0, R8
|
||||||
|
MOVV R0, R9
|
||||||
|
MOVV trap+0(FP), R2 // syscall entry
|
||||||
|
SYSCALL
|
||||||
|
MOVV R2, r1+32(FP)
|
||||||
|
MOVV R3, r2+40(FP)
|
||||||
|
RET
|
||||||
|
|
23
vendor/golang.org/x/sys/unix/asm_linux_mipsx.s
generated
vendored
23
vendor/golang.org/x/sys/unix/asm_linux_mipsx.s
generated
vendored
|
@ -24,8 +24,31 @@ TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||||
JMP syscall·Syscall9(SB)
|
JMP syscall·Syscall9(SB)
|
||||||
|
|
||||||
|
TEXT ·SyscallNoError(SB),NOSPLIT,$0-24
|
||||||
|
JAL runtime·entersyscall(SB)
|
||||||
|
MOVW a1+4(FP), R4
|
||||||
|
MOVW a2+8(FP), R5
|
||||||
|
MOVW a3+12(FP), R6
|
||||||
|
MOVW R0, R7
|
||||||
|
MOVW trap+0(FP), R2 // syscall entry
|
||||||
|
SYSCALL
|
||||||
|
MOVW R2, r1+16(FP) // r1
|
||||||
|
MOVW R3, r2+20(FP) // r2
|
||||||
|
JAL runtime·exitsyscall(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||||
JMP syscall·RawSyscall(SB)
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||||
JMP syscall·RawSyscall6(SB)
|
JMP syscall·RawSyscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-24
|
||||||
|
MOVW a1+4(FP), R4
|
||||||
|
MOVW a2+8(FP), R5
|
||||||
|
MOVW a3+12(FP), R6
|
||||||
|
MOVW trap+0(FP), R2 // syscall entry
|
||||||
|
SYSCALL
|
||||||
|
MOVW R2, r1+16(FP)
|
||||||
|
MOVW R3, r2+20(FP)
|
||||||
|
RET
|
||||||
|
|
36
vendor/golang.org/x/sys/unix/asm_linux_ppc64x.s
generated
vendored
36
vendor/golang.org/x/sys/unix/asm_linux_ppc64x.s
generated
vendored
|
@ -15,14 +15,30 @@
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
// The runtime may know about them.
|
// The runtime may know about them.
|
||||||
|
|
||||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||||
BR syscall·Syscall(SB)
|
BL runtime·entersyscall(SB)
|
||||||
|
MOVD a1+8(FP), R3
|
||||||
|
MOVD a2+16(FP), R4
|
||||||
|
MOVD a3+24(FP), R5
|
||||||
|
MOVD R0, R6
|
||||||
|
MOVD R0, R7
|
||||||
|
MOVD R0, R8
|
||||||
|
MOVD trap+0(FP), R9 // syscall entry
|
||||||
|
SYSCALL R9
|
||||||
|
MOVD R3, r1+32(FP)
|
||||||
|
MOVD R4, r2+40(FP)
|
||||||
|
BL runtime·exitsyscall(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||||
BR syscall·Syscall6(SB)
|
MOVD a1+8(FP), R3
|
||||||
|
MOVD a2+16(FP), R4
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
MOVD a3+24(FP), R5
|
||||||
BR syscall·RawSyscall(SB)
|
MOVD R0, R6
|
||||||
|
MOVD R0, R7
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
MOVD R0, R8
|
||||||
BR syscall·RawSyscall6(SB)
|
MOVD trap+0(FP), R9 // syscall entry
|
||||||
|
SYSCALL R9
|
||||||
|
MOVD R3, r1+32(FP)
|
||||||
|
MOVD R4, r2+40(FP)
|
||||||
|
RET
|
||||||
|
|
28
vendor/golang.org/x/sys/unix/asm_linux_s390x.s
generated
vendored
28
vendor/golang.org/x/sys/unix/asm_linux_s390x.s
generated
vendored
|
@ -21,8 +21,36 @@ TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||||
BR syscall·Syscall6(SB)
|
BR syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
||||||
|
BL runtime·entersyscall(SB)
|
||||||
|
MOVD a1+8(FP), R2
|
||||||
|
MOVD a2+16(FP), R3
|
||||||
|
MOVD a3+24(FP), R4
|
||||||
|
MOVD $0, R5
|
||||||
|
MOVD $0, R6
|
||||||
|
MOVD $0, R7
|
||||||
|
MOVD trap+0(FP), R1 // syscall entry
|
||||||
|
SYSCALL
|
||||||
|
MOVD R2, r1+32(FP)
|
||||||
|
MOVD R3, r2+40(FP)
|
||||||
|
BL runtime·exitsyscall(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||||
BR syscall·RawSyscall(SB)
|
BR syscall·RawSyscall(SB)
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||||
BR syscall·RawSyscall6(SB)
|
BR syscall·RawSyscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
||||||
|
MOVD a1+8(FP), R2
|
||||||
|
MOVD a2+16(FP), R3
|
||||||
|
MOVD a3+24(FP), R4
|
||||||
|
MOVD $0, R5
|
||||||
|
MOVD $0, R6
|
||||||
|
MOVD $0, R7
|
||||||
|
MOVD trap+0(FP), R1 // syscall entry
|
||||||
|
SYSCALL
|
||||||
|
MOVD R2, r1+32(FP)
|
||||||
|
MOVD R3, r2+40(FP)
|
||||||
|
RET
|
||||||
|
|
29
vendor/golang.org/x/sys/unix/asm_netbsd_arm64.s
generated
vendored
Normal file
29
vendor/golang.org/x/sys/unix/asm_netbsd_arm64.s
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright 2019 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 !gccgo
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// System call support for ARM64, NetBSD
|
||||||
|
//
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||||
|
B syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||||
|
B syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||||
|
B syscall·Syscall9(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||||
|
B syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||||
|
B syscall·RawSyscall6(SB)
|
30
vendor/golang.org/x/sys/unix/cap_freebsd.go
generated
vendored
30
vendor/golang.org/x/sys/unix/cap_freebsd.go
generated
vendored
|
@ -7,7 +7,7 @@
|
||||||
package unix
|
package unix
|
||||||
|
|
||||||
import (
|
import (
|
||||||
errorspkg "errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -60,26 +60,26 @@ func CapRightsSet(rights *CapRights, setrights []uint64) error {
|
||||||
|
|
||||||
n := caparsize(rights)
|
n := caparsize(rights)
|
||||||
if n < capArSizeMin || n > capArSizeMax {
|
if n < capArSizeMin || n > capArSizeMax {
|
||||||
return errorspkg.New("bad rights size")
|
return errors.New("bad rights size")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, right := range setrights {
|
for _, right := range setrights {
|
||||||
if caprver(right) != CAP_RIGHTS_VERSION_00 {
|
if caprver(right) != CAP_RIGHTS_VERSION_00 {
|
||||||
return errorspkg.New("bad right version")
|
return errors.New("bad right version")
|
||||||
}
|
}
|
||||||
i, err := rightToIndex(right)
|
i, err := rightToIndex(right)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if i >= n {
|
if i >= n {
|
||||||
return errorspkg.New("index overflow")
|
return errors.New("index overflow")
|
||||||
}
|
}
|
||||||
if capidxbit(rights.Rights[i]) != capidxbit(right) {
|
if capidxbit(rights.Rights[i]) != capidxbit(right) {
|
||||||
return errorspkg.New("index mismatch")
|
return errors.New("index mismatch")
|
||||||
}
|
}
|
||||||
rights.Rights[i] |= right
|
rights.Rights[i] |= right
|
||||||
if capidxbit(rights.Rights[i]) != capidxbit(right) {
|
if capidxbit(rights.Rights[i]) != capidxbit(right) {
|
||||||
return errorspkg.New("index mismatch (after assign)")
|
return errors.New("index mismatch (after assign)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,26 +95,26 @@ func CapRightsClear(rights *CapRights, clearrights []uint64) error {
|
||||||
|
|
||||||
n := caparsize(rights)
|
n := caparsize(rights)
|
||||||
if n < capArSizeMin || n > capArSizeMax {
|
if n < capArSizeMin || n > capArSizeMax {
|
||||||
return errorspkg.New("bad rights size")
|
return errors.New("bad rights size")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, right := range clearrights {
|
for _, right := range clearrights {
|
||||||
if caprver(right) != CAP_RIGHTS_VERSION_00 {
|
if caprver(right) != CAP_RIGHTS_VERSION_00 {
|
||||||
return errorspkg.New("bad right version")
|
return errors.New("bad right version")
|
||||||
}
|
}
|
||||||
i, err := rightToIndex(right)
|
i, err := rightToIndex(right)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if i >= n {
|
if i >= n {
|
||||||
return errorspkg.New("index overflow")
|
return errors.New("index overflow")
|
||||||
}
|
}
|
||||||
if capidxbit(rights.Rights[i]) != capidxbit(right) {
|
if capidxbit(rights.Rights[i]) != capidxbit(right) {
|
||||||
return errorspkg.New("index mismatch")
|
return errors.New("index mismatch")
|
||||||
}
|
}
|
||||||
rights.Rights[i] &= ^(right & 0x01FFFFFFFFFFFFFF)
|
rights.Rights[i] &= ^(right & 0x01FFFFFFFFFFFFFF)
|
||||||
if capidxbit(rights.Rights[i]) != capidxbit(right) {
|
if capidxbit(rights.Rights[i]) != capidxbit(right) {
|
||||||
return errorspkg.New("index mismatch (after assign)")
|
return errors.New("index mismatch (after assign)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,22 +130,22 @@ func CapRightsIsSet(rights *CapRights, setrights []uint64) (bool, error) {
|
||||||
|
|
||||||
n := caparsize(rights)
|
n := caparsize(rights)
|
||||||
if n < capArSizeMin || n > capArSizeMax {
|
if n < capArSizeMin || n > capArSizeMax {
|
||||||
return false, errorspkg.New("bad rights size")
|
return false, errors.New("bad rights size")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, right := range setrights {
|
for _, right := range setrights {
|
||||||
if caprver(right) != CAP_RIGHTS_VERSION_00 {
|
if caprver(right) != CAP_RIGHTS_VERSION_00 {
|
||||||
return false, errorspkg.New("bad right version")
|
return false, errors.New("bad right version")
|
||||||
}
|
}
|
||||||
i, err := rightToIndex(right)
|
i, err := rightToIndex(right)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
if i >= n {
|
if i >= n {
|
||||||
return false, errorspkg.New("index overflow")
|
return false, errors.New("index overflow")
|
||||||
}
|
}
|
||||||
if capidxbit(rights.Rights[i]) != capidxbit(right) {
|
if capidxbit(rights.Rights[i]) != capidxbit(right) {
|
||||||
return false, errorspkg.New("index mismatch")
|
return false, errors.New("index mismatch")
|
||||||
}
|
}
|
||||||
if (rights.Rights[i] & right) != right {
|
if (rights.Rights[i] & right) != right {
|
||||||
return false, nil
|
return false, nil
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user