TUN-4961: Update quic-go to latest
- Updates fips-go to be the latest on cfsetup.yaml - Updates sumtype's x/tools to be latest to avoid Internal: nil pkg errors with fips.
This commit is contained in:
parent
d0a1daac3b
commit
414cb12f02
|
@ -1,5 +1,5 @@
|
|||
pinned_go: &pinned_go go=1.16.3-1
|
||||
pinned_go_fips: &pinned_go_fips go-fips=1.15.5-3
|
||||
pinned_go: &pinned_go go=1.17
|
||||
pinned_go_fips: &pinned_go_fips go-boring=1.16.6-6
|
||||
|
||||
build_dir: &build_dir /cfsetup_build
|
||||
default-flavor: buster
|
||||
|
@ -116,7 +116,7 @@ stretch: &stretch
|
|||
- gotest-to-teamcity
|
||||
pre-cache:
|
||||
- go get golang.org/x/tools/cmd/goimports
|
||||
- go get github.com/BurntSushi/go-sumtype
|
||||
- go get github.com/sudarshan-reddy/go-sumtype@v0.0.0-20210827105221-82eca7e5abb1
|
||||
post-cache:
|
||||
- export GOOS=linux
|
||||
- export GOARCH=amd64
|
||||
|
|
9
go.mod
9
go.mod
|
@ -22,14 +22,13 @@ require (
|
|||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/gobwas/ws v1.0.4
|
||||
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
|
||||
github.com/google/go-cmp v0.5.2 // indirect
|
||||
github.com/google/uuid v1.1.2
|
||||
github.com/gorilla/mux v1.7.3
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/json-iterator/go v1.1.10
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/lucas-clemente/quic-go v0.20.0
|
||||
github.com/lucas-clemente/quic-go v0.23.0
|
||||
github.com/mattn/go-colorable v0.1.8
|
||||
github.com/miekg/dns v1.1.31
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
|
@ -47,9 +46,9 @@ require (
|
|||
github.com/urfave/cli/v2 v2.2.0
|
||||
go.uber.org/automaxprocs v1.4.0
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
|
||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 // indirect
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf
|
||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d // indirect
|
||||
|
@ -58,7 +57,7 @@ require (
|
|||
gopkg.in/coreos/go-oidc.v2 v2.1.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
gopkg.in/square/go-jose.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.3.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
|
||||
zombiezen.com/go/capnproto2 v2.18.0+incompatible
|
||||
)
|
||||
|
|
40
go.sum
40
go.sum
|
@ -234,6 +234,8 @@ github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dp
|
|||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/gobwas/httphead v0.0.0-20200921212729-da3d93bc3c58 h1:YyrUZvJaU8Q0QsoVo+xLFBgWDTam29PKea6GYmwvSiQ=
|
||||
github.com/gobwas/httphead v0.0.0-20200921212729-da3d93bc3c58/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
|
||||
|
@ -265,6 +267,7 @@ github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
|
|||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
|
||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
|
||||
|
@ -281,6 +284,9 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
|
|||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
|
@ -293,6 +299,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
|
@ -417,6 +424,8 @@ github.com/liquidweb/liquidweb-go v1.6.0/go.mod h1:UDcVnAMDkZxpw4Y7NOHkqoeiGacVL
|
|||
github.com/lucas-clemente/quic-go v0.13.1/go.mod h1:Vn3/Fb0/77b02SGhQk36KzOUmXgVpFfizUfW5WMaqyU=
|
||||
github.com/lucas-clemente/quic-go v0.20.0 h1:FSU3YN5VnLafHR27Ejs1r1CYMS7XMyIVDzRewkDLNBw=
|
||||
github.com/lucas-clemente/quic-go v0.20.0/go.mod h1:fZq/HUDIM+mW6X6wtzORjC0E/WDBMKe5Hf9bgjISwLk=
|
||||
github.com/lucas-clemente/quic-go v0.23.0 h1:5vFnKtZ6nHDFsc/F3uuiF4T3y/AXaQdxjUqiVw26GZE=
|
||||
github.com/lucas-clemente/quic-go v0.23.0/go.mod h1:paZuzjXCE5mj6sikVLMvqXk8lJV2AsqtJ6bDhjEfxx0=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
|
@ -432,6 +441,10 @@ github.com/marten-seemann/qtls-go1-15 v0.1.4 h1:RehYMOyRW8hPVEja1KBVsFVNSm35Jj9M
|
|||
github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.3 h1:XEZ1xGorVy9u+lJq+WXNE+hiqRYLNvJGYmwfwKQN2gU=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.3/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.4 h1:xbHbOGGhrenVtII6Co8akhLEdrawwB2iHl5yhJRpnco=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.4/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.0 h1:P9ggrs5xtwiqXv/FHNwntmuLMNq3KaSIG93AtAZ48xk=
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.0/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
|
@ -493,6 +506,8 @@ github.com/nrdcg/goinwx v0.6.1/go.mod h1:XPiut7enlbEdntAqalBIqcYcTEVhpv/dKWgDCX2
|
|||
github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw=
|
||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
|
@ -504,12 +519,16 @@ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
|
|||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
|
||||
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
|
||||
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
||||
|
@ -672,6 +691,8 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q
|
|||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||
go.etcd.io/etcd v0.5.0-alpha.5.0.20200306183522-221f0cc107cb/go.mod h1:VZB9Yx4s43MHItytoe8jcvaEFEgF2QzHDZGfQ/XQjvQ=
|
||||
|
@ -752,6 +773,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
|||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -800,8 +823,11 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
|
|||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA=
|
||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
|
@ -821,6 +847,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -879,9 +906,12 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
|
@ -894,6 +924,8 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
|
|||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
@ -951,6 +983,9 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY
|
|||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.1 h1:wGiQel/hW0NnEkJUk8lbzkX2gFJU6PFxf1v5OlCfuOs=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -1055,6 +1090,9 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
|
|||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/DataDog/dd-trace-go.v1 v1.24.1/go.mod h1:DVp8HmDh8PuTu2Z0fVVlBsyWaC++fzwVCaGWylTe3tg=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
@ -1094,6 +1132,8 @@ gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# 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
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
genny
|
|
@ -0,0 +1,6 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.7
|
||||
- 1.8
|
||||
- 1.9
|
|
@ -0,0 +1,245 @@
|
|||
# genny - Generics for Go
|
||||
|
||||
[![Build Status](https://travis-ci.org/cheekybits/genny.svg?branch=master)](https://travis-ci.org/cheekybits/genny) [![GoDoc](https://godoc.org/github.com/cheekybits/genny/parse?status.png)](http://godoc.org/github.com/cheekybits/genny/parse)
|
||||
|
||||
Install:
|
||||
|
||||
```
|
||||
go get github.com/cheekybits/genny
|
||||
```
|
||||
|
||||
=====
|
||||
|
||||
(pron. Jenny) by Mat Ryer ([@matryer](https://twitter.com/matryer)) and Tyler Bunnell ([@TylerJBunnell](https://twitter.com/TylerJBunnell)).
|
||||
|
||||
Until the Go core team include support for [generics in Go](http://golang.org/doc/faq#generics), `genny` is a code-generation generics solution. It allows you write normal buildable and testable Go code which, when processed by the `genny gen` tool, will replace the generics with specific types.
|
||||
|
||||
* Generic code is valid Go code
|
||||
* Generic code compiles and can be tested
|
||||
* Use `stdin` and `stdout` or specify in and out files
|
||||
* Supports Go 1.4's [go generate](http://tip.golang.org/doc/go1.4#gogenerate)
|
||||
* Multiple specific types will generate every permutation
|
||||
* Use `BUILTINS` and `NUMBERS` wildtype to generate specific code for all built-in (and number) Go types
|
||||
* Function names and comments also get updated
|
||||
|
||||
## Library
|
||||
|
||||
We have started building a [library of common things](https://github.com/cheekybits/gennylib), and you can use `genny get` to generate the specific versions you need.
|
||||
|
||||
For example: `genny get maps/concurrentmap.go "KeyType=BUILTINS ValueType=BUILTINS"` will print out generated code for all types for a concurrent map. Any file in the library may be generated locally in this way using all the same options given to `genny gen`.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
genny [{flags}] gen "{types}"
|
||||
|
||||
gen - generates type specific code from generic code.
|
||||
get <package/file> - fetch a generic template from the online library and gen it.
|
||||
|
||||
{flags} - (optional) Command line flags (see below)
|
||||
{types} - (required) Specific types for each generic type in the source
|
||||
{types} format: {generic}={specific}[,another][ {generic2}={specific2}]
|
||||
|
||||
Examples:
|
||||
Generic=Specific
|
||||
Generic1=Specific1 Generic2=Specific2
|
||||
Generic1=Specific1,Specific2 Generic2=Specific3,Specific4
|
||||
|
||||
Flags:
|
||||
-in="": file to parse instead of stdin
|
||||
-out="": file to save output to instead of stdout
|
||||
-pkg="": package name for generated files
|
||||
```
|
||||
|
||||
* Comma separated type lists will generate code for each type
|
||||
|
||||
### Flags
|
||||
|
||||
* `-in` - specify the input file (rather than using stdin)
|
||||
* `-out` - specify the output file (rather than using stdout)
|
||||
|
||||
### go generate
|
||||
|
||||
To use Go 1.4's `go generate` capability, insert the following comment in your source code file:
|
||||
|
||||
```
|
||||
//go:generate genny -in=$GOFILE -out=gen-$GOFILE gen "KeyType=string,int ValueType=string,int"
|
||||
```
|
||||
|
||||
* Start the line with `//go:generate `
|
||||
* Use the `-in` and `-out` flags to specify the files to work on
|
||||
* Use the `genny` command as usual after the flags
|
||||
|
||||
Now, running `go generate` (in a shell) for the package will cause the generic versions of the files to be generated.
|
||||
|
||||
* The output file will be overwritten, so it's safe to call `go generate` many times
|
||||
* Use `$GOFILE` to refer to the current file
|
||||
* The `//go:generate` line will be removed from the output
|
||||
|
||||
To see a real example of how to use `genny` with `go generate`, look in the [example/go-generate directory](https://github.com/cheekybits/genny/tree/master/examples/go-generate).
|
||||
|
||||
## How it works
|
||||
|
||||
Define your generic types using the special `generic.Type` placeholder type:
|
||||
|
||||
```go
|
||||
type KeyType generic.Type
|
||||
type ValueType generic.Type
|
||||
```
|
||||
|
||||
* You can use as many as you like
|
||||
* Give them meaningful names
|
||||
|
||||
Then write the generic code referencing the types as your normally would:
|
||||
|
||||
```go
|
||||
func SetValueTypeForKeyType(key KeyType, value ValueType) { /* ... */ }
|
||||
```
|
||||
|
||||
* Generic type names will also be replaced in comments and function names (see Real example below)
|
||||
|
||||
Since `generic.Type` is a real Go type, your code will compile, and you can even write unit tests against your generic code.
|
||||
|
||||
#### Generating specific versions
|
||||
|
||||
Pass the file through the `genny gen` tool with the specific types as the argument:
|
||||
|
||||
```
|
||||
cat generic.go | genny gen "KeyType=string ValueType=interface{}"
|
||||
```
|
||||
|
||||
The output will be the complete Go source file with the generic types replaced with the types specified in the arguments.
|
||||
|
||||
## Real example
|
||||
|
||||
Given [this generic Go code](https://github.com/cheekybits/genny/tree/master/examples/queue) which compiles and is tested:
|
||||
|
||||
```go
|
||||
package queue
|
||||
|
||||
import "github.com/cheekybits/genny/generic"
|
||||
|
||||
// NOTE: this is how easy it is to define a generic type
|
||||
type Something generic.Type
|
||||
|
||||
// SomethingQueue is a queue of Somethings.
|
||||
type SomethingQueue struct {
|
||||
items []Something
|
||||
}
|
||||
|
||||
func NewSomethingQueue() *SomethingQueue {
|
||||
return &SomethingQueue{items: make([]Something, 0)}
|
||||
}
|
||||
func (q *SomethingQueue) Push(item Something) {
|
||||
q.items = append(q.items, item)
|
||||
}
|
||||
func (q *SomethingQueue) Pop() Something {
|
||||
item := q.items[0]
|
||||
q.items = q.items[1:]
|
||||
return item
|
||||
}
|
||||
```
|
||||
|
||||
When `genny gen` is invoked like this:
|
||||
|
||||
```
|
||||
cat source.go | genny gen "Something=string"
|
||||
```
|
||||
|
||||
It outputs:
|
||||
|
||||
```go
|
||||
// This file was automatically generated by genny.
|
||||
// Any changes will be lost if this file is regenerated.
|
||||
// see https://github.com/cheekybits/genny
|
||||
|
||||
package queue
|
||||
|
||||
// StringQueue is a queue of Strings.
|
||||
type StringQueue struct {
|
||||
items []string
|
||||
}
|
||||
|
||||
func NewStringQueue() *StringQueue {
|
||||
return &StringQueue{items: make([]string, 0)}
|
||||
}
|
||||
func (q *StringQueue) Push(item string) {
|
||||
q.items = append(q.items, item)
|
||||
}
|
||||
func (q *StringQueue) Pop() string {
|
||||
item := q.items[0]
|
||||
q.items = q.items[1:]
|
||||
return item
|
||||
}
|
||||
```
|
||||
|
||||
To get a _something_ for every built-in Go type plus one of your own types, you could run:
|
||||
|
||||
```
|
||||
cat source.go | genny gen "Something=BUILTINS,*MyType"
|
||||
```
|
||||
|
||||
#### More examples
|
||||
|
||||
Check out the [test code files](https://github.com/cheekybits/genny/tree/master/parse/test) for more real examples.
|
||||
|
||||
## Writing test code
|
||||
|
||||
Once you have defined a generic type with some code worth testing:
|
||||
|
||||
```go
|
||||
package slice
|
||||
|
||||
import (
|
||||
"log"
|
||||
"reflect"
|
||||
|
||||
"github.com/stretchr/gogen/generic"
|
||||
)
|
||||
|
||||
type MyType generic.Type
|
||||
|
||||
func EnsureMyTypeSlice(objectOrSlice interface{}) []MyType {
|
||||
log.Printf("%v", reflect.TypeOf(objectOrSlice))
|
||||
switch obj := objectOrSlice.(type) {
|
||||
case []MyType:
|
||||
log.Println(" returning it untouched")
|
||||
return obj
|
||||
case MyType:
|
||||
log.Println(" wrapping in slice")
|
||||
return []MyType{obj}
|
||||
default:
|
||||
panic("ensure slice needs MyType or []MyType")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can treat it like any normal Go type in your test code:
|
||||
|
||||
```go
|
||||
func TestEnsureMyTypeSlice(t *testing.T) {
|
||||
|
||||
myType := new(MyType)
|
||||
slice := EnsureMyTypeSlice(myType)
|
||||
if assert.NotNil(t, slice) {
|
||||
assert.Equal(t, slice[0], myType)
|
||||
}
|
||||
|
||||
slice = EnsureMyTypeSlice(slice)
|
||||
log.Printf("%#v", slice[0])
|
||||
if assert.NotNil(t, slice) {
|
||||
assert.Equal(t, slice[0], myType)
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
### Understanding what `generic.Type` is
|
||||
|
||||
Because `generic.Type` is an empty interface type (literally `interface{}`) every other type will be considered to be a `generic.Type` if you are switching on the type of an object. Of course, once the specific versions are generated, this issue goes away but it's worth knowing when you are writing your tests against generic code.
|
||||
|
||||
### Contributions
|
||||
|
||||
* See the [API documentation for the parse package](http://godoc.org/github.com/cheekybits/genny/parse)
|
||||
* Please do TDD
|
||||
* All input welcome
|
|
@ -0,0 +1,2 @@
|
|||
// Package main is the command line tool for Genny.
|
||||
package main
|
|
@ -0,0 +1,154 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/cheekybits/genny/out"
|
||||
"github.com/cheekybits/genny/parse"
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
source | genny gen [-in=""] [-out=""] [-pkg=""] "KeyType=string,int ValueType=string,int"
|
||||
|
||||
*/
|
||||
|
||||
const (
|
||||
_ = iota
|
||||
exitcodeInvalidArgs
|
||||
exitcodeInvalidTypeSet
|
||||
exitcodeStdinFailed
|
||||
exitcodeGenFailed
|
||||
exitcodeGetFailed
|
||||
exitcodeSourceFileInvalid
|
||||
exitcodeDestFileFailed
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
in = flag.String("in", "", "file to parse instead of stdin")
|
||||
out = flag.String("out", "", "file to save output to instead of stdout")
|
||||
pkgName = flag.String("pkg", "", "package name for generated files")
|
||||
prefix = "https://github.com/metabition/gennylib/raw/master/"
|
||||
)
|
||||
flag.Parse()
|
||||
args := flag.Args()
|
||||
|
||||
if len(args) < 2 {
|
||||
usage()
|
||||
os.Exit(exitcodeInvalidArgs)
|
||||
}
|
||||
|
||||
if strings.ToLower(args[0]) != "gen" && strings.ToLower(args[0]) != "get" {
|
||||
usage()
|
||||
os.Exit(exitcodeInvalidArgs)
|
||||
}
|
||||
|
||||
// parse the typesets
|
||||
var setsArg = args[1]
|
||||
if strings.ToLower(args[0]) == "get" {
|
||||
setsArg = args[2]
|
||||
}
|
||||
typeSets, err := parse.TypeSet(setsArg)
|
||||
if err != nil {
|
||||
fatal(exitcodeInvalidTypeSet, err)
|
||||
}
|
||||
|
||||
outWriter := newWriter(*out)
|
||||
|
||||
if strings.ToLower(args[0]) == "get" {
|
||||
if len(args) != 3 {
|
||||
fmt.Println("not enough arguments to get")
|
||||
usage()
|
||||
os.Exit(exitcodeInvalidArgs)
|
||||
}
|
||||
r, err := http.Get(prefix + args[1])
|
||||
if err != nil {
|
||||
fatal(exitcodeGetFailed, err)
|
||||
}
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
fatal(exitcodeGetFailed, err)
|
||||
}
|
||||
r.Body.Close()
|
||||
br := bytes.NewReader(b)
|
||||
err = gen(*in, *pkgName, br, typeSets, outWriter)
|
||||
} else if len(*in) > 0 {
|
||||
var file *os.File
|
||||
file, err = os.Open(*in)
|
||||
if err != nil {
|
||||
fatal(exitcodeSourceFileInvalid, err)
|
||||
}
|
||||
defer file.Close()
|
||||
err = gen(*in, *pkgName, file, typeSets, outWriter)
|
||||
} else {
|
||||
var source []byte
|
||||
source, err = ioutil.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
fatal(exitcodeStdinFailed, err)
|
||||
}
|
||||
reader := bytes.NewReader(source)
|
||||
err = gen("stdin", *pkgName, reader, typeSets, outWriter)
|
||||
}
|
||||
|
||||
// do the work
|
||||
if err != nil {
|
||||
fatal(exitcodeGenFailed, err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintln(os.Stderr, `usage: genny [{flags}] gen "{types}"
|
||||
|
||||
gen - generates type specific code from generic code.
|
||||
get <package/file> - fetch a generic template from the online library and gen it.
|
||||
|
||||
{flags} - (optional) Command line flags (see below)
|
||||
{types} - (required) Specific types for each generic type in the source
|
||||
{types} format: {generic}={specific}[,another][ {generic2}={specific2}]
|
||||
|
||||
Examples:
|
||||
Generic=Specific
|
||||
Generic1=Specific1 Generic2=Specific2
|
||||
Generic1=Specific1,Specific2 Generic2=Specific3,Specific4
|
||||
|
||||
Flags:`)
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
func newWriter(fileName string) io.Writer {
|
||||
if fileName == "" {
|
||||
return os.Stdout
|
||||
}
|
||||
lf := &out.LazyFile{FileName: fileName}
|
||||
defer lf.Close()
|
||||
return lf
|
||||
}
|
||||
|
||||
func fatal(code int, a ...interface{}) {
|
||||
fmt.Println(a...)
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
// gen performs the generic generation.
|
||||
func gen(filename, pkgName string, in io.ReadSeeker, typesets []map[string]string, out io.Writer) error {
|
||||
|
||||
var output []byte
|
||||
var err error
|
||||
|
||||
output, err = parse.Generics(filename, pkgName, in, typesets)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out.Write(output)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package out
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
// LazyFile is an io.WriteCloser which defers creation of the file it is supposed to write in
|
||||
// till the first call to its write function in order to prevent creation of file, if no write
|
||||
// is supposed to happen.
|
||||
type LazyFile struct {
|
||||
// FileName is path to the file to which genny will write.
|
||||
FileName string
|
||||
file *os.File
|
||||
}
|
||||
|
||||
// Close closes the file if it is created. Returns nil if no file is created.
|
||||
func (lw *LazyFile) Close() error {
|
||||
if lw.file != nil {
|
||||
return lw.file.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write writes to the specified file and creates the file first time it is called.
|
||||
func (lw *LazyFile) Write(p []byte) (int, error) {
|
||||
if lw.file == nil {
|
||||
err := os.MkdirAll(path.Dir(lw.FileName), 0755)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
lw.file, err = os.Create(lw.FileName)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return lw.file.Write(p)
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package parse
|
||||
|
||||
// Builtins contains a slice of all built-in Go types.
|
||||
var Builtins = []string{
|
||||
"bool",
|
||||
"byte",
|
||||
"complex128",
|
||||
"complex64",
|
||||
"error",
|
||||
"float32",
|
||||
"float64",
|
||||
"int",
|
||||
"int16",
|
||||
"int32",
|
||||
"int64",
|
||||
"int8",
|
||||
"rune",
|
||||
"string",
|
||||
"uint",
|
||||
"uint16",
|
||||
"uint32",
|
||||
"uint64",
|
||||
"uint8",
|
||||
"uintptr",
|
||||
}
|
||||
|
||||
// Numbers contains a slice of all built-in number types.
|
||||
var Numbers = []string{
|
||||
"float32",
|
||||
"float64",
|
||||
"int",
|
||||
"int16",
|
||||
"int32",
|
||||
"int64",
|
||||
"int8",
|
||||
"uint",
|
||||
"uint16",
|
||||
"uint32",
|
||||
"uint64",
|
||||
"uint8",
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// Package parse contains the generic code generation capabilities
|
||||
// that power genny.
|
||||
//
|
||||
// genny gen "{types}"
|
||||
//
|
||||
// gen - generates type specific code (to stdout) from generic code (via stdin)
|
||||
//
|
||||
// {types} - (required) Specific types for each generic type in the source
|
||||
// {types} format: {generic}={specific}[,another][ {generic2}={specific2}]
|
||||
// Examples:
|
||||
// Generic=Specific
|
||||
// Generic1=Specific1 Generic2=Specific2
|
||||
// Generic1=Specific1,Specific2 Generic2=Specific3,Specific4
|
||||
package parse
|
|
@ -0,0 +1,47 @@
|
|||
package parse
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// errMissingSpecificType represents an error when a generic type is not
|
||||
// satisfied by a specific type.
|
||||
type errMissingSpecificType struct {
|
||||
GenericType string
|
||||
}
|
||||
|
||||
// Error gets a human readable string describing this error.
|
||||
func (e errMissingSpecificType) Error() string {
|
||||
return "Missing specific type for '" + e.GenericType + "' generic type"
|
||||
}
|
||||
|
||||
// errImports represents an error from goimports.
|
||||
type errImports struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
// Error gets a human readable string describing this error.
|
||||
func (e errImports) Error() string {
|
||||
return "Failed to goimports the generated code: " + e.Err.Error()
|
||||
}
|
||||
|
||||
// errSource represents an error with the source file.
|
||||
type errSource struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
// Error gets a human readable string describing this error.
|
||||
func (e errSource) Error() string {
|
||||
return "Failed to parse source file: " + e.Err.Error()
|
||||
}
|
||||
|
||||
type errBadTypeArgs struct {
|
||||
Message string
|
||||
Arg string
|
||||
}
|
||||
|
||||
func (e errBadTypeArgs) Error() string {
|
||||
return "\"" + e.Arg + "\" is bad: " + e.Message
|
||||
}
|
||||
|
||||
var errMissingTypeInformation = errors.New("No type arguments were specified and no \"// +gogen\" tag was found in the source.")
|
|
@ -0,0 +1,298 @@
|
|||
package parse
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/tools/imports"
|
||||
)
|
||||
|
||||
var header = []byte(`
|
||||
|
||||
// This file was automatically generated by genny.
|
||||
// Any changes will be lost if this file is regenerated.
|
||||
// see https://github.com/cheekybits/genny
|
||||
|
||||
`)
|
||||
|
||||
var (
|
||||
packageKeyword = []byte("package")
|
||||
importKeyword = []byte("import")
|
||||
openBrace = []byte("(")
|
||||
closeBrace = []byte(")")
|
||||
genericPackage = "generic"
|
||||
genericType = "generic.Type"
|
||||
genericNumber = "generic.Number"
|
||||
linefeed = "\r\n"
|
||||
)
|
||||
var unwantedLinePrefixes = [][]byte{
|
||||
[]byte("//go:generate genny "),
|
||||
}
|
||||
|
||||
func subIntoLiteral(lit, typeTemplate, specificType string) string {
|
||||
if lit == typeTemplate {
|
||||
return specificType
|
||||
}
|
||||
if !strings.Contains(lit, typeTemplate) {
|
||||
return lit
|
||||
}
|
||||
specificLg := wordify(specificType, true)
|
||||
specificSm := wordify(specificType, false)
|
||||
result := strings.Replace(lit, typeTemplate, specificLg, -1)
|
||||
if strings.HasPrefix(result, specificLg) && !isExported(lit) {
|
||||
return strings.Replace(result, specificLg, specificSm, 1)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func subTypeIntoComment(line, typeTemplate, specificType string) string {
|
||||
var subbed string
|
||||
for _, w := range strings.Fields(line) {
|
||||
subbed = subbed + subIntoLiteral(w, typeTemplate, specificType) + " "
|
||||
}
|
||||
return subbed
|
||||
}
|
||||
|
||||
// Does the heavy lifting of taking a line of our code and
|
||||
// sbustituting a type into there for our generic type
|
||||
func subTypeIntoLine(line, typeTemplate, specificType string) string {
|
||||
src := []byte(line)
|
||||
var s scanner.Scanner
|
||||
fset := token.NewFileSet()
|
||||
file := fset.AddFile("", fset.Base(), len(src))
|
||||
s.Init(file, src, nil, scanner.ScanComments)
|
||||
output := ""
|
||||
for {
|
||||
_, tok, lit := s.Scan()
|
||||
if tok == token.EOF {
|
||||
break
|
||||
} else if tok == token.COMMENT {
|
||||
subbed := subTypeIntoComment(lit, typeTemplate, specificType)
|
||||
output = output + subbed + " "
|
||||
} else if tok.IsLiteral() {
|
||||
subbed := subIntoLiteral(lit, typeTemplate, specificType)
|
||||
output = output + subbed + " "
|
||||
} else {
|
||||
output = output + tok.String() + " "
|
||||
}
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
// typeSet looks like "KeyType: int, ValueType: string"
|
||||
func generateSpecific(filename string, in io.ReadSeeker, typeSet map[string]string) ([]byte, error) {
|
||||
|
||||
// ensure we are at the beginning of the file
|
||||
in.Seek(0, os.SEEK_SET)
|
||||
|
||||
// parse the source file
|
||||
fs := token.NewFileSet()
|
||||
file, err := parser.ParseFile(fs, filename, in, 0)
|
||||
if err != nil {
|
||||
return nil, &errSource{Err: err}
|
||||
}
|
||||
|
||||
// make sure every generic.Type is represented in the types
|
||||
// argument.
|
||||
for _, decl := range file.Decls {
|
||||
switch it := decl.(type) {
|
||||
case *ast.GenDecl:
|
||||
for _, spec := range it.Specs {
|
||||
ts, ok := spec.(*ast.TypeSpec)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
switch tt := ts.Type.(type) {
|
||||
case *ast.SelectorExpr:
|
||||
if name, ok := tt.X.(*ast.Ident); ok {
|
||||
if name.Name == genericPackage {
|
||||
if _, ok := typeSet[ts.Name.Name]; !ok {
|
||||
return nil, &errMissingSpecificType{GenericType: ts.Name.Name}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
in.Seek(0, os.SEEK_SET)
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
comment := ""
|
||||
scanner := bufio.NewScanner(in)
|
||||
for scanner.Scan() {
|
||||
|
||||
line := scanner.Text()
|
||||
|
||||
// does this line contain generic.Type?
|
||||
if strings.Contains(line, genericType) || strings.Contains(line, genericNumber) {
|
||||
comment = ""
|
||||
continue
|
||||
}
|
||||
|
||||
for t, specificType := range typeSet {
|
||||
if strings.Contains(line, t) {
|
||||
newLine := subTypeIntoLine(line, t, specificType)
|
||||
line = newLine
|
||||
}
|
||||
}
|
||||
|
||||
if comment != "" {
|
||||
buf.WriteString(makeLine(comment))
|
||||
comment = ""
|
||||
}
|
||||
|
||||
// is this line a comment?
|
||||
// TODO: should we handle /* */ comments?
|
||||
if strings.HasPrefix(line, "//") {
|
||||
// record this line to print later
|
||||
comment = line
|
||||
continue
|
||||
}
|
||||
|
||||
// write the line
|
||||
buf.WriteString(makeLine(line))
|
||||
}
|
||||
|
||||
// write it out
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// Generics parses the source file and generates the bytes replacing the
|
||||
// generic types for the keys map with the specific types (its value).
|
||||
func Generics(filename, pkgName string, in io.ReadSeeker, typeSets []map[string]string) ([]byte, error) {
|
||||
|
||||
totalOutput := header
|
||||
|
||||
for _, typeSet := range typeSets {
|
||||
|
||||
// generate the specifics
|
||||
parsed, err := generateSpecific(filename, in, typeSet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
totalOutput = append(totalOutput, parsed...)
|
||||
|
||||
}
|
||||
|
||||
// clean up the code line by line
|
||||
packageFound := false
|
||||
insideImportBlock := false
|
||||
var cleanOutputLines []string
|
||||
scanner := bufio.NewScanner(bytes.NewReader(totalOutput))
|
||||
for scanner.Scan() {
|
||||
|
||||
// end of imports block?
|
||||
if insideImportBlock {
|
||||
if bytes.HasSuffix(scanner.Bytes(), closeBrace) {
|
||||
insideImportBlock = false
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if bytes.HasPrefix(scanner.Bytes(), packageKeyword) {
|
||||
if packageFound {
|
||||
continue
|
||||
} else {
|
||||
packageFound = true
|
||||
}
|
||||
} else if bytes.HasPrefix(scanner.Bytes(), importKeyword) {
|
||||
if bytes.HasSuffix(scanner.Bytes(), openBrace) {
|
||||
insideImportBlock = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// check all unwantedLinePrefixes - and skip them
|
||||
skipline := false
|
||||
for _, prefix := range unwantedLinePrefixes {
|
||||
if bytes.HasPrefix(scanner.Bytes(), prefix) {
|
||||
skipline = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if skipline {
|
||||
continue
|
||||
}
|
||||
|
||||
cleanOutputLines = append(cleanOutputLines, makeLine(scanner.Text()))
|
||||
}
|
||||
|
||||
cleanOutput := strings.Join(cleanOutputLines, "")
|
||||
|
||||
output := []byte(cleanOutput)
|
||||
var err error
|
||||
|
||||
// change package name
|
||||
if pkgName != "" {
|
||||
output = changePackage(bytes.NewReader([]byte(output)), pkgName)
|
||||
}
|
||||
// fix the imports
|
||||
output, err = imports.Process(filename, output, nil)
|
||||
if err != nil {
|
||||
return nil, &errImports{Err: err}
|
||||
}
|
||||
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func makeLine(s string) string {
|
||||
return fmt.Sprintln(strings.TrimRight(s, linefeed))
|
||||
}
|
||||
|
||||
// isAlphaNumeric gets whether the rune is alphanumeric or _.
|
||||
func isAlphaNumeric(r rune) bool {
|
||||
return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r)
|
||||
}
|
||||
|
||||
// wordify turns a type into a nice word for function and type
|
||||
// names etc.
|
||||
func wordify(s string, exported bool) string {
|
||||
s = strings.TrimRight(s, "{}")
|
||||
s = strings.TrimLeft(s, "*&")
|
||||
s = strings.Replace(s, ".", "", -1)
|
||||
if !exported {
|
||||
return s
|
||||
}
|
||||
return strings.ToUpper(string(s[0])) + s[1:]
|
||||
}
|
||||
|
||||
func changePackage(r io.Reader, pkgName string) []byte {
|
||||
var out bytes.Buffer
|
||||
sc := bufio.NewScanner(r)
|
||||
done := false
|
||||
|
||||
for sc.Scan() {
|
||||
s := sc.Text()
|
||||
|
||||
if !done && strings.HasPrefix(s, "package") {
|
||||
parts := strings.Split(s, " ")
|
||||
parts[1] = pkgName
|
||||
s = strings.Join(parts, " ")
|
||||
done = true
|
||||
}
|
||||
|
||||
fmt.Fprintln(&out, s)
|
||||
}
|
||||
return out.Bytes()
|
||||
}
|
||||
|
||||
func isExported(lit string) bool {
|
||||
if len(lit) == 0 {
|
||||
return false
|
||||
}
|
||||
return unicode.IsUpper(rune(lit[0]))
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package parse
|
||||
|
||||
import "strings"
|
||||
|
||||
const (
|
||||
typeSep = " "
|
||||
keyValueSep = "="
|
||||
valuesSep = ","
|
||||
builtins = "BUILTINS"
|
||||
numbers = "NUMBERS"
|
||||
)
|
||||
|
||||
// TypeSet turns a type string into a []map[string]string
|
||||
// that can be given to parse.Generics for it to do its magic.
|
||||
//
|
||||
// Acceptable args are:
|
||||
//
|
||||
// Person=man
|
||||
// Person=man Animal=dog
|
||||
// Person=man Animal=dog Animal2=cat
|
||||
// Person=man,woman Animal=dog,cat
|
||||
// Person=man,woman,child Animal=dog,cat Place=london,paris
|
||||
func TypeSet(arg string) ([]map[string]string, error) {
|
||||
|
||||
types := make(map[string][]string)
|
||||
var keys []string
|
||||
for _, pair := range strings.Split(arg, typeSep) {
|
||||
segs := strings.Split(pair, keyValueSep)
|
||||
if len(segs) != 2 {
|
||||
return nil, &errBadTypeArgs{Arg: arg, Message: "Generic=Specific expected"}
|
||||
}
|
||||
key := segs[0]
|
||||
keys = append(keys, key)
|
||||
types[key] = make([]string, 0)
|
||||
for _, t := range strings.Split(segs[1], valuesSep) {
|
||||
if t == builtins {
|
||||
types[key] = append(types[key], Builtins...)
|
||||
} else if t == numbers {
|
||||
types[key] = append(types[key], Numbers...)
|
||||
} else {
|
||||
types[key] = append(types[key], t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cursors := make(map[string]int)
|
||||
for _, key := range keys {
|
||||
cursors[key] = 0
|
||||
}
|
||||
|
||||
outChan := make(chan map[string]string)
|
||||
go func() {
|
||||
buildTypeSet(keys, 0, cursors, types, outChan)
|
||||
close(outChan)
|
||||
}()
|
||||
|
||||
var typeSets []map[string]string
|
||||
for typeSet := range outChan {
|
||||
typeSets = append(typeSets, typeSet)
|
||||
}
|
||||
|
||||
return typeSets, nil
|
||||
|
||||
}
|
||||
|
||||
func buildTypeSet(keys []string, keyI int, cursors map[string]int, types map[string][]string, out chan<- map[string]string) {
|
||||
key := keys[keyI]
|
||||
for cursors[key] < len(types[key]) {
|
||||
if keyI < len(keys)-1 {
|
||||
buildTypeSet(keys, keyI+1, copycursors(cursors), types, out)
|
||||
} else {
|
||||
// build the typeset for this combination
|
||||
ts := make(map[string]string)
|
||||
for k, vals := range types {
|
||||
ts[k] = vals[cursors[k]]
|
||||
}
|
||||
out <- ts
|
||||
}
|
||||
cursors[key]++
|
||||
}
|
||||
}
|
||||
|
||||
func copycursors(source map[string]int) map[string]int {
|
||||
copy := make(map[string]int)
|
||||
for k, v := range source {
|
||||
copy[k] = v
|
||||
}
|
||||
return copy
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = tab
|
||||
indent_size = 8
|
||||
|
||||
[*.{md,yml,yaml,json}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
|
@ -0,0 +1 @@
|
|||
* text=auto
|
|
@ -0,0 +1,2 @@
|
|||
vendor/
|
||||
/.glide
|
|
@ -0,0 +1,364 @@
|
|||
# Changelog
|
||||
|
||||
## Release 3.2.0 (2020-12-14)
|
||||
|
||||
### Added
|
||||
|
||||
- #211: Added randInt function (thanks @kochurovro)
|
||||
- #223: Added fromJson and mustFromJson functions (thanks @mholt)
|
||||
- #242: Added a bcrypt function (thanks @robbiet480)
|
||||
- #253: Added randBytes function (thanks @MikaelSmith)
|
||||
- #254: Added dig function for dicts (thanks @nyarly)
|
||||
- #257: Added regexQuoteMeta for quoting regex metadata (thanks @rheaton)
|
||||
- #261: Added filepath functions osBase, osDir, osExt, osClean, osIsAbs (thanks @zugl)
|
||||
- #268: Added and and all functions for testing conditions (thanks @phuslu)
|
||||
- #181: Added float64 arithmetic addf, add1f, subf, divf, mulf, maxf, and minf
|
||||
(thanks @andrewmostello)
|
||||
- #265: Added chunk function to split array into smaller arrays (thanks @karelbilek)
|
||||
- #270: Extend certificate functions to handle non-RSA keys + add support for
|
||||
ed25519 keys (thanks @misberner)
|
||||
|
||||
### Changed
|
||||
|
||||
- Removed testing and support for Go 1.12. ed25519 support requires Go 1.13 or newer
|
||||
- Using semver 3.1.1 and mergo 0.3.11
|
||||
|
||||
### Fixed
|
||||
|
||||
- #249: Fix htmlDateInZone example (thanks @spawnia)
|
||||
|
||||
NOTE: The dependency github.com/imdario/mergo reverted the breaking change in
|
||||
0.3.9 via 0.3.10 release.
|
||||
|
||||
## Release 3.1.0 (2020-04-16)
|
||||
|
||||
NOTE: The dependency github.com/imdario/mergo made a behavior change in 0.3.9
|
||||
that impacts sprig functionality. Do not use sprig with a version newer than 0.3.8.
|
||||
|
||||
### Added
|
||||
|
||||
- #225: Added support for generating htpasswd hash (thanks @rustycl0ck)
|
||||
- #224: Added duration filter (thanks @frebib)
|
||||
- #205: Added `seq` function (thanks @thadc23)
|
||||
|
||||
### Changed
|
||||
|
||||
- #203: Unlambda functions with correct signature (thanks @muesli)
|
||||
- #236: Updated the license formatting for GitHub display purposes
|
||||
- #238: Updated package dependency versions. Note, mergo not updated to 0.3.9
|
||||
as it causes a breaking change for sprig. That issue is tracked at
|
||||
https://github.com/imdario/mergo/issues/139
|
||||
|
||||
### Fixed
|
||||
|
||||
- #229: Fix `seq` example in docs (thanks @kalmant)
|
||||
|
||||
## Release 3.0.2 (2019-12-13)
|
||||
|
||||
### Fixed
|
||||
|
||||
- #220: Updating to semver v3.0.3 to fix issue with <= ranges
|
||||
- #218: fix typo elyptical->elliptic in ecdsa key description (thanks @laverya)
|
||||
|
||||
## Release 3.0.1 (2019-12-08)
|
||||
|
||||
### Fixed
|
||||
|
||||
- #212: Updated semver fixing broken constraint checking with ^0.0
|
||||
|
||||
## Release 3.0.0 (2019-10-02)
|
||||
|
||||
### Added
|
||||
|
||||
- #187: Added durationRound function (thanks @yjp20)
|
||||
- #189: Added numerous template functions that return errors rather than panic (thanks @nrvnrvn)
|
||||
- #193: Added toRawJson support (thanks @Dean-Coakley)
|
||||
- #197: Added get support to dicts (thanks @Dean-Coakley)
|
||||
|
||||
### Changed
|
||||
|
||||
- #186: Moving dependency management to Go modules
|
||||
- #186: Updated semver to v3. This has changes in the way ^ is handled
|
||||
- #194: Updated documentation on merging and how it copies. Added example using deepCopy
|
||||
- #196: trunc now supports negative values (thanks @Dean-Coakley)
|
||||
|
||||
## Release 2.22.0 (2019-10-02)
|
||||
|
||||
### Added
|
||||
|
||||
- #173: Added getHostByName function to resolve dns names to ips (thanks @fcgravalos)
|
||||
- #195: Added deepCopy function for use with dicts
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated merge and mergeOverwrite documentation to explain copying and how to
|
||||
use deepCopy with it
|
||||
|
||||
## Release 2.21.0 (2019-09-18)
|
||||
|
||||
### Added
|
||||
|
||||
- #122: Added encryptAES/decryptAES functions (thanks @n0madic)
|
||||
- #128: Added toDecimal support (thanks @Dean-Coakley)
|
||||
- #169: Added list contcat (thanks @astorath)
|
||||
- #174: Added deepEqual function (thanks @bonifaido)
|
||||
- #170: Added url parse and join functions (thanks @astorath)
|
||||
|
||||
### Changed
|
||||
|
||||
- #171: Updated glide config for Google UUID to v1 and to add ranges to semver and testify
|
||||
|
||||
### Fixed
|
||||
|
||||
- #172: Fix semver wildcard example (thanks @piepmatz)
|
||||
- #175: Fix dateInZone doc example (thanks @s3than)
|
||||
|
||||
## Release 2.20.0 (2019-06-18)
|
||||
|
||||
### Added
|
||||
|
||||
- #164: Adding function to get unix epoch for a time (@mattfarina)
|
||||
- #166: Adding tests for date_in_zone (@mattfarina)
|
||||
|
||||
### Changed
|
||||
|
||||
- #144: Fix function comments based on best practices from Effective Go (@CodeLingoTeam)
|
||||
- #150: Handles pointer type for time.Time in "htmlDate" (@mapreal19)
|
||||
- #161, #157, #160, #153, #158, #156, #155, #159, #152 documentation updates (@badeadan)
|
||||
|
||||
### Fixed
|
||||
|
||||
## Release 2.19.0 (2019-03-02)
|
||||
|
||||
IMPORTANT: This release reverts a change from 2.18.0
|
||||
|
||||
In the previous release (2.18), we prematurely merged a partial change to the crypto functions that led to creating two sets of crypto functions (I blame @technosophos -- since that's me). This release rolls back that change, and does what was originally intended: It alters the existing crypto functions to use secure random.
|
||||
|
||||
We debated whether this classifies as a change worthy of major revision, but given the proximity to the last release, we have decided that treating 2.18 as a faulty release is the correct course of action. We apologize for any inconvenience.
|
||||
|
||||
### Changed
|
||||
|
||||
- Fix substr panic 35fb796 (Alexey igrychev)
|
||||
- Remove extra period 1eb7729 (Matthew Lorimor)
|
||||
- Make random string functions use crypto by default 6ceff26 (Matthew Lorimor)
|
||||
- README edits/fixes/suggestions 08fe136 (Lauri Apple)
|
||||
|
||||
|
||||
## Release 2.18.0 (2019-02-12)
|
||||
|
||||
### Added
|
||||
|
||||
- Added mergeOverwrite function
|
||||
- cryptographic functions that use secure random (see fe1de12)
|
||||
|
||||
### Changed
|
||||
|
||||
- Improve documentation of regexMatch function, resolves #139 90b89ce (Jan Tagscherer)
|
||||
- Handle has for nil list 9c10885 (Daniel Cohen)
|
||||
- Document behaviour of mergeOverwrite fe0dbe9 (Lukas Rieder)
|
||||
- doc: adds missing documentation. 4b871e6 (Fernandez Ludovic)
|
||||
- Replace outdated goutils imports 01893d2 (Matthew Lorimor)
|
||||
- Surface crypto secure random strings from goutils fe1de12 (Matthew Lorimor)
|
||||
- Handle untyped nil values as paramters to string functions 2b2ec8f (Morten Torkildsen)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix dict merge issue and provide mergeOverwrite .dst .src1 to overwrite from src -> dst 4c59c12 (Lukas Rieder)
|
||||
- Fix substr var names and comments d581f80 (Dean Coakley)
|
||||
- Fix substr documentation 2737203 (Dean Coakley)
|
||||
|
||||
## Release 2.17.1 (2019-01-03)
|
||||
|
||||
### Fixed
|
||||
|
||||
The 2.17.0 release did not have a version pinned for xstrings, which caused compilation failures when xstrings < 1.2 was used. This adds the correct version string to glide.yaml.
|
||||
|
||||
## Release 2.17.0 (2019-01-03)
|
||||
|
||||
### Added
|
||||
|
||||
- adds alder32sum function and test 6908fc2 (marshallford)
|
||||
- Added kebabcase function ca331a1 (Ilyes512)
|
||||
|
||||
### Changed
|
||||
|
||||
- Update goutils to 1.1.0 4e1125d (Matt Butcher)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix 'has' documentation e3f2a85 (dean-coakley)
|
||||
- docs(dict): fix typo in pick example dc424f9 (Dustin Specker)
|
||||
- fixes spelling errors... not sure how that happened 4cf188a (marshallford)
|
||||
|
||||
## Release 2.16.0 (2018-08-13)
|
||||
|
||||
### Added
|
||||
|
||||
- add splitn function fccb0b0 (Helgi Þorbjörnsson)
|
||||
- Add slice func df28ca7 (gongdo)
|
||||
- Generate serial number a3bdffd (Cody Coons)
|
||||
- Extract values of dict with values function df39312 (Lawrence Jones)
|
||||
|
||||
### Changed
|
||||
|
||||
- Modify panic message for list.slice ae38335 (gongdo)
|
||||
- Minor improvement in code quality - Removed an unreachable piece of code at defaults.go#L26:6 - Resolve formatting issues. 5834241 (Abhishek Kashyap)
|
||||
- Remove duplicated documentation 1d97af1 (Matthew Fisher)
|
||||
- Test on go 1.11 49df809 (Helgi Þormar Þorbjörnsson)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix file permissions c5f40b5 (gongdo)
|
||||
- Fix example for buildCustomCert 7779e0d (Tin Lam)
|
||||
|
||||
## Release 2.15.0 (2018-04-02)
|
||||
|
||||
### Added
|
||||
|
||||
- #68 and #69: Add json helpers to docs (thanks @arunvelsriram)
|
||||
- #66: Add ternary function (thanks @binoculars)
|
||||
- #67: Allow keys function to take multiple dicts (thanks @binoculars)
|
||||
- #89: Added sha1sum to crypto function (thanks @benkeil)
|
||||
- #81: Allow customizing Root CA that used by genSignedCert (thanks @chenzhiwei)
|
||||
- #92: Add travis testing for go 1.10
|
||||
- #93: Adding appveyor config for windows testing
|
||||
|
||||
### Changed
|
||||
|
||||
- #90: Updating to more recent dependencies
|
||||
- #73: replace satori/go.uuid with google/uuid (thanks @petterw)
|
||||
|
||||
### Fixed
|
||||
|
||||
- #76: Fixed documentation typos (thanks @Thiht)
|
||||
- Fixed rounding issue on the `ago` function. Note, the removes support for Go 1.8 and older
|
||||
|
||||
## Release 2.14.1 (2017-12-01)
|
||||
|
||||
### Fixed
|
||||
|
||||
- #60: Fix typo in function name documentation (thanks @neil-ca-moore)
|
||||
- #61: Removing line with {{ due to blocking github pages genertion
|
||||
- #64: Update the list functions to handle int, string, and other slices for compatibility
|
||||
|
||||
## Release 2.14.0 (2017-10-06)
|
||||
|
||||
This new version of Sprig adds a set of functions for generating and working with SSL certificates.
|
||||
|
||||
- `genCA` generates an SSL Certificate Authority
|
||||
- `genSelfSignedCert` generates an SSL self-signed certificate
|
||||
- `genSignedCert` generates an SSL certificate and key based on a given CA
|
||||
|
||||
## Release 2.13.0 (2017-09-18)
|
||||
|
||||
This release adds new functions, including:
|
||||
|
||||
- `regexMatch`, `regexFindAll`, `regexFind`, `regexReplaceAll`, `regexReplaceAllLiteral`, and `regexSplit` to work with regular expressions
|
||||
- `floor`, `ceil`, and `round` math functions
|
||||
- `toDate` converts a string to a date
|
||||
- `nindent` is just like `indent` but also prepends a new line
|
||||
- `ago` returns the time from `time.Now`
|
||||
|
||||
### Added
|
||||
|
||||
- #40: Added basic regex functionality (thanks @alanquillin)
|
||||
- #41: Added ceil floor and round functions (thanks @alanquillin)
|
||||
- #48: Added toDate function (thanks @andreynering)
|
||||
- #50: Added nindent function (thanks @binoculars)
|
||||
- #46: Added ago function (thanks @slayer)
|
||||
|
||||
### Changed
|
||||
|
||||
- #51: Updated godocs to include new string functions (thanks @curtisallen)
|
||||
- #49: Added ability to merge multiple dicts (thanks @binoculars)
|
||||
|
||||
## Release 2.12.0 (2017-05-17)
|
||||
|
||||
- `snakecase`, `camelcase`, and `shuffle` are three new string functions
|
||||
- `fail` allows you to bail out of a template render when conditions are not met
|
||||
|
||||
## Release 2.11.0 (2017-05-02)
|
||||
|
||||
- Added `toJson` and `toPrettyJson`
|
||||
- Added `merge`
|
||||
- Refactored documentation
|
||||
|
||||
## Release 2.10.0 (2017-03-15)
|
||||
|
||||
- Added `semver` and `semverCompare` for Semantic Versions
|
||||
- `list` replaces `tuple`
|
||||
- Fixed issue with `join`
|
||||
- Added `first`, `last`, `intial`, `rest`, `prepend`, `append`, `toString`, `toStrings`, `sortAlpha`, `reverse`, `coalesce`, `pluck`, `pick`, `compact`, `keys`, `omit`, `uniq`, `has`, `without`
|
||||
|
||||
## Release 2.9.0 (2017-02-23)
|
||||
|
||||
- Added `splitList` to split a list
|
||||
- Added crypto functions of `genPrivateKey` and `derivePassword`
|
||||
|
||||
## Release 2.8.0 (2016-12-21)
|
||||
|
||||
- Added access to several path functions (`base`, `dir`, `clean`, `ext`, and `abs`)
|
||||
- Added functions for _mutating_ dictionaries (`set`, `unset`, `hasKey`)
|
||||
|
||||
## Release 2.7.0 (2016-12-01)
|
||||
|
||||
- Added `sha256sum` to generate a hash of an input
|
||||
- Added functions to convert a numeric or string to `int`, `int64`, `float64`
|
||||
|
||||
## Release 2.6.0 (2016-10-03)
|
||||
|
||||
- Added a `uuidv4` template function for generating UUIDs inside of a template.
|
||||
|
||||
## Release 2.5.0 (2016-08-19)
|
||||
|
||||
- New `trimSuffix`, `trimPrefix`, `hasSuffix`, and `hasPrefix` functions
|
||||
- New aliases have been added for a few functions that didn't follow the naming conventions (`trimAll` and `abbrevBoth`)
|
||||
- `trimall` and `abbrevboth` (notice the case) are deprecated and will be removed in 3.0.0
|
||||
|
||||
## Release 2.4.0 (2016-08-16)
|
||||
|
||||
- Adds two functions: `until` and `untilStep`
|
||||
|
||||
## Release 2.3.0 (2016-06-21)
|
||||
|
||||
- cat: Concatenate strings with whitespace separators.
|
||||
- replace: Replace parts of a string: `replace " " "-" "Me First"` renders "Me-First"
|
||||
- plural: Format plurals: `len "foo" | plural "one foo" "many foos"` renders "many foos"
|
||||
- indent: Indent blocks of text in a way that is sensitive to "\n" characters.
|
||||
|
||||
## Release 2.2.0 (2016-04-21)
|
||||
|
||||
- Added a `genPrivateKey` function (Thanks @bacongobbler)
|
||||
|
||||
## Release 2.1.0 (2016-03-30)
|
||||
|
||||
- `default` now prints the default value when it does not receive a value down the pipeline. It is much safer now to do `{{.Foo | default "bar"}}`.
|
||||
- Added accessors for "hermetic" functions. These return only functions that, when given the same input, produce the same output.
|
||||
|
||||
## Release 2.0.0 (2016-03-29)
|
||||
|
||||
Because we switched from `int` to `int64` as the return value for all integer math functions, the library's major version number has been incremented.
|
||||
|
||||
- `min` complements `max` (formerly `biggest`)
|
||||
- `empty` indicates that a value is the empty value for its type
|
||||
- `tuple` creates a tuple inside of a template: `{{$t := tuple "a", "b" "c"}}`
|
||||
- `dict` creates a dictionary inside of a template `{{$d := dict "key1" "val1" "key2" "val2"}}`
|
||||
- Date formatters have been added for HTML dates (as used in `date` input fields)
|
||||
- Integer math functions can convert from a number of types, including `string` (via `strconv.ParseInt`).
|
||||
|
||||
## Release 1.2.0 (2016-02-01)
|
||||
|
||||
- Added quote and squote
|
||||
- Added b32enc and b32dec
|
||||
- add now takes varargs
|
||||
- biggest now takes varargs
|
||||
|
||||
## Release 1.1.0 (2015-12-29)
|
||||
|
||||
- Added #4: Added contains function. strings.Contains, but with the arguments
|
||||
switched to simplify common pipelines. (thanks krancour)
|
||||
- Added Travis-CI testing support
|
||||
|
||||
## Release 1.0.0 (2015-12-23)
|
||||
|
||||
- Initial release
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (C) 2013-2020 Masterminds
|
||||
|
||||
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.
|
|
@ -0,0 +1,73 @@
|
|||
# Slim-Sprig: Template functions for Go templates [![GoDoc](https://godoc.org/github.com/go-task/slim-sprig?status.svg)](https://godoc.org/github.com/go-task/slim-sprig) [![Go Report Card](https://goreportcard.com/badge/github.com/go-task/slim-sprig)](https://goreportcard.com/report/github.com/go-task/slim-sprig)
|
||||
|
||||
Slim-Sprig is a fork of [Sprig](https://github.com/Masterminds/sprig), but with
|
||||
all functions that depend on external (non standard library) or crypto packages
|
||||
removed.
|
||||
The reason for this is to make this library more lightweight. Most of these
|
||||
functions (specially crypto ones) are not needed on most apps, but costs a lot
|
||||
in terms of binary size and compilation time.
|
||||
|
||||
## Usage
|
||||
|
||||
**Template developers**: Please use Slim-Sprig's [function documentation](https://go-task.github.io/slim-sprig/) for
|
||||
detailed instructions and code snippets for the >100 template functions available.
|
||||
|
||||
**Go developers**: If you'd like to include Slim-Sprig as a library in your program,
|
||||
our API documentation is available [at GoDoc.org](http://godoc.org/github.com/go-task/slim-sprig).
|
||||
|
||||
For standard usage, read on.
|
||||
|
||||
### Load the Slim-Sprig library
|
||||
|
||||
To load the Slim-Sprig `FuncMap`:
|
||||
|
||||
```go
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
|
||||
"github.com/go-task/slim-sprig"
|
||||
)
|
||||
|
||||
// This example illustrates that the FuncMap *must* be set before the
|
||||
// templates themselves are loaded.
|
||||
tpl := template.Must(
|
||||
template.New("base").Funcs(sprig.FuncMap()).ParseGlob("*.html")
|
||||
)
|
||||
```
|
||||
|
||||
### Calling the functions inside of templates
|
||||
|
||||
By convention, all functions are lowercase. This seems to follow the Go
|
||||
idiom for template functions (as opposed to template methods, which are
|
||||
TitleCase). For example, this:
|
||||
|
||||
```
|
||||
{{ "hello!" | upper | repeat 5 }}
|
||||
```
|
||||
|
||||
produces this:
|
||||
|
||||
```
|
||||
HELLO!HELLO!HELLO!HELLO!HELLO!
|
||||
```
|
||||
|
||||
## Principles Driving Our Function Selection
|
||||
|
||||
We followed these principles to decide which functions to add and how to implement them:
|
||||
|
||||
- Use template functions to build layout. The following
|
||||
types of operations are within the domain of template functions:
|
||||
- Formatting
|
||||
- Layout
|
||||
- Simple type conversions
|
||||
- Utilities that assist in handling common formatting and layout needs (e.g. arithmetic)
|
||||
- Template functions should not return errors unless there is no way to print
|
||||
a sensible value. For example, converting a string to an integer should not
|
||||
produce an error if conversion fails. Instead, it should display a default
|
||||
value.
|
||||
- Simple math is necessary for grid layouts, pagers, and so on. Complex math
|
||||
(anything other than arithmetic) should be done outside of templates.
|
||||
- Template functions only deal with the data passed into them. They never retrieve
|
||||
data from a source.
|
||||
- Finally, do not override core Go template functions.
|
|
@ -0,0 +1,12 @@
|
|||
# https://taskfile.dev
|
||||
|
||||
version: '2'
|
||||
|
||||
tasks:
|
||||
default:
|
||||
cmds:
|
||||
- task: test
|
||||
|
||||
test:
|
||||
cmds:
|
||||
- go test -v .
|
|
@ -0,0 +1,24 @@
|
|||
package sprig
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash/adler32"
|
||||
)
|
||||
|
||||
func sha256sum(input string) string {
|
||||
hash := sha256.Sum256([]byte(input))
|
||||
return hex.EncodeToString(hash[:])
|
||||
}
|
||||
|
||||
func sha1sum(input string) string {
|
||||
hash := sha1.Sum([]byte(input))
|
||||
return hex.EncodeToString(hash[:])
|
||||
}
|
||||
|
||||
func adler32sum(input string) string {
|
||||
hash := adler32.Checksum([]byte(input))
|
||||
return fmt.Sprintf("%d", hash)
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
package sprig
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Given a format and a date, format the date string.
|
||||
//
|
||||
// Date can be a `time.Time` or an `int, int32, int64`.
|
||||
// In the later case, it is treated as seconds since UNIX
|
||||
// epoch.
|
||||
func date(fmt string, date interface{}) string {
|
||||
return dateInZone(fmt, date, "Local")
|
||||
}
|
||||
|
||||
func htmlDate(date interface{}) string {
|
||||
return dateInZone("2006-01-02", date, "Local")
|
||||
}
|
||||
|
||||
func htmlDateInZone(date interface{}, zone string) string {
|
||||
return dateInZone("2006-01-02", date, zone)
|
||||
}
|
||||
|
||||
func dateInZone(fmt string, date interface{}, zone string) string {
|
||||
var t time.Time
|
||||
switch date := date.(type) {
|
||||
default:
|
||||
t = time.Now()
|
||||
case time.Time:
|
||||
t = date
|
||||
case *time.Time:
|
||||
t = *date
|
||||
case int64:
|
||||
t = time.Unix(date, 0)
|
||||
case int:
|
||||
t = time.Unix(int64(date), 0)
|
||||
case int32:
|
||||
t = time.Unix(int64(date), 0)
|
||||
}
|
||||
|
||||
loc, err := time.LoadLocation(zone)
|
||||
if err != nil {
|
||||
loc, _ = time.LoadLocation("UTC")
|
||||
}
|
||||
|
||||
return t.In(loc).Format(fmt)
|
||||
}
|
||||
|
||||
func dateModify(fmt string, date time.Time) time.Time {
|
||||
d, err := time.ParseDuration(fmt)
|
||||
if err != nil {
|
||||
return date
|
||||
}
|
||||
return date.Add(d)
|
||||
}
|
||||
|
||||
func mustDateModify(fmt string, date time.Time) (time.Time, error) {
|
||||
d, err := time.ParseDuration(fmt)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
return date.Add(d), nil
|
||||
}
|
||||
|
||||
func dateAgo(date interface{}) string {
|
||||
var t time.Time
|
||||
|
||||
switch date := date.(type) {
|
||||
default:
|
||||
t = time.Now()
|
||||
case time.Time:
|
||||
t = date
|
||||
case int64:
|
||||
t = time.Unix(date, 0)
|
||||
case int:
|
||||
t = time.Unix(int64(date), 0)
|
||||
}
|
||||
// Drop resolution to seconds
|
||||
duration := time.Since(t).Round(time.Second)
|
||||
return duration.String()
|
||||
}
|
||||
|
||||
func duration(sec interface{}) string {
|
||||
var n int64
|
||||
switch value := sec.(type) {
|
||||
default:
|
||||
n = 0
|
||||
case string:
|
||||
n, _ = strconv.ParseInt(value, 10, 64)
|
||||
case int64:
|
||||
n = value
|
||||
}
|
||||
return (time.Duration(n) * time.Second).String()
|
||||
}
|
||||
|
||||
func durationRound(duration interface{}) string {
|
||||
var d time.Duration
|
||||
switch duration := duration.(type) {
|
||||
default:
|
||||
d = 0
|
||||
case string:
|
||||
d, _ = time.ParseDuration(duration)
|
||||
case int64:
|
||||
d = time.Duration(duration)
|
||||
case time.Time:
|
||||
d = time.Since(duration)
|
||||
}
|
||||
|
||||
u := uint64(d)
|
||||
neg := d < 0
|
||||
if neg {
|
||||
u = -u
|
||||
}
|
||||
|
||||
var (
|
||||
year = uint64(time.Hour) * 24 * 365
|
||||
month = uint64(time.Hour) * 24 * 30
|
||||
day = uint64(time.Hour) * 24
|
||||
hour = uint64(time.Hour)
|
||||
minute = uint64(time.Minute)
|
||||
second = uint64(time.Second)
|
||||
)
|
||||
switch {
|
||||
case u > year:
|
||||
return strconv.FormatUint(u/year, 10) + "y"
|
||||
case u > month:
|
||||
return strconv.FormatUint(u/month, 10) + "mo"
|
||||
case u > day:
|
||||
return strconv.FormatUint(u/day, 10) + "d"
|
||||
case u > hour:
|
||||
return strconv.FormatUint(u/hour, 10) + "h"
|
||||
case u > minute:
|
||||
return strconv.FormatUint(u/minute, 10) + "m"
|
||||
case u > second:
|
||||
return strconv.FormatUint(u/second, 10) + "s"
|
||||
}
|
||||
return "0s"
|
||||
}
|
||||
|
||||
func toDate(fmt, str string) time.Time {
|
||||
t, _ := time.ParseInLocation(fmt, str, time.Local)
|
||||
return t
|
||||
}
|
||||
|
||||
func mustToDate(fmt, str string) (time.Time, error) {
|
||||
return time.ParseInLocation(fmt, str, time.Local)
|
||||
}
|
||||
|
||||
func unixEpoch(date time.Time) string {
|
||||
return strconv.FormatInt(date.Unix(), 10)
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
package sprig
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
// dfault checks whether `given` is set, and returns default if not set.
|
||||
//
|
||||
// This returns `d` if `given` appears not to be set, and `given` otherwise.
|
||||
//
|
||||
// For numeric types 0 is unset.
|
||||
// For strings, maps, arrays, and slices, len() = 0 is considered unset.
|
||||
// For bool, false is unset.
|
||||
// Structs are never considered unset.
|
||||
//
|
||||
// For everything else, including pointers, a nil value is unset.
|
||||
func dfault(d interface{}, given ...interface{}) interface{} {
|
||||
|
||||
if empty(given) || empty(given[0]) {
|
||||
return d
|
||||
}
|
||||
return given[0]
|
||||
}
|
||||
|
||||
// empty returns true if the given value has the zero value for its type.
|
||||
func empty(given interface{}) bool {
|
||||
g := reflect.ValueOf(given)
|
||||
if !g.IsValid() {
|
||||
return true
|
||||
}
|
||||
|
||||
// Basically adapted from text/template.isTrue
|
||||
switch g.Kind() {
|
||||
default:
|
||||
return g.IsNil()
|
||||
case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
|
||||
return g.Len() == 0
|
||||
case reflect.Bool:
|
||||
return !g.Bool()
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
return g.Complex() == 0
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return g.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return g.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return g.Float() == 0
|
||||
case reflect.Struct:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// coalesce returns the first non-empty value.
|
||||
func coalesce(v ...interface{}) interface{} {
|
||||
for _, val := range v {
|
||||
if !empty(val) {
|
||||
return val
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// all returns true if empty(x) is false for all values x in the list.
|
||||
// If the list is empty, return true.
|
||||
func all(v ...interface{}) bool {
|
||||
for _, val := range v {
|
||||
if empty(val) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// any returns true if empty(x) is false for any x in the list.
|
||||
// If the list is empty, return false.
|
||||
func any(v ...interface{}) bool {
|
||||
for _, val := range v {
|
||||
if !empty(val) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// fromJson decodes JSON into a structured value, ignoring errors.
|
||||
func fromJson(v string) interface{} {
|
||||
output, _ := mustFromJson(v)
|
||||
return output
|
||||
}
|
||||
|
||||
// mustFromJson decodes JSON into a structured value, returning errors.
|
||||
func mustFromJson(v string) (interface{}, error) {
|
||||
var output interface{}
|
||||
err := json.Unmarshal([]byte(v), &output)
|
||||
return output, err
|
||||
}
|
||||
|
||||
// toJson encodes an item into a JSON string
|
||||
func toJson(v interface{}) string {
|
||||
output, _ := json.Marshal(v)
|
||||
return string(output)
|
||||
}
|
||||
|
||||
func mustToJson(v interface{}) (string, error) {
|
||||
output, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(output), nil
|
||||
}
|
||||
|
||||
// toPrettyJson encodes an item into a pretty (indented) JSON string
|
||||
func toPrettyJson(v interface{}) string {
|
||||
output, _ := json.MarshalIndent(v, "", " ")
|
||||
return string(output)
|
||||
}
|
||||
|
||||
func mustToPrettyJson(v interface{}) (string, error) {
|
||||
output, err := json.MarshalIndent(v, "", " ")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(output), nil
|
||||
}
|
||||
|
||||
// toRawJson encodes an item into a JSON string with no escaping of HTML characters.
|
||||
func toRawJson(v interface{}) string {
|
||||
output, err := mustToRawJson(v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return string(output)
|
||||
}
|
||||
|
||||
// mustToRawJson encodes an item into a JSON string with no escaping of HTML characters.
|
||||
func mustToRawJson(v interface{}) (string, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
enc := json.NewEncoder(buf)
|
||||
enc.SetEscapeHTML(false)
|
||||
err := enc.Encode(&v)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSuffix(buf.String(), "\n"), nil
|
||||
}
|
||||
|
||||
// ternary returns the first value if the last value is true, otherwise returns the second value.
|
||||
func ternary(vt interface{}, vf interface{}, v bool) interface{} {
|
||||
if v {
|
||||
return vt
|
||||
}
|
||||
|
||||
return vf
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
package sprig
|
||||
|
||||
func get(d map[string]interface{}, key string) interface{} {
|
||||
if val, ok := d[key]; ok {
|
||||
return val
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func set(d map[string]interface{}, key string, value interface{}) map[string]interface{} {
|
||||
d[key] = value
|
||||
return d
|
||||
}
|
||||
|
||||
func unset(d map[string]interface{}, key string) map[string]interface{} {
|
||||
delete(d, key)
|
||||
return d
|
||||
}
|
||||
|
||||
func hasKey(d map[string]interface{}, key string) bool {
|
||||
_, ok := d[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
func pluck(key string, d ...map[string]interface{}) []interface{} {
|
||||
res := []interface{}{}
|
||||
for _, dict := range d {
|
||||
if val, ok := dict[key]; ok {
|
||||
res = append(res, val)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func keys(dicts ...map[string]interface{}) []string {
|
||||
k := []string{}
|
||||
for _, dict := range dicts {
|
||||
for key := range dict {
|
||||
k = append(k, key)
|
||||
}
|
||||
}
|
||||
return k
|
||||
}
|
||||
|
||||
func pick(dict map[string]interface{}, keys ...string) map[string]interface{} {
|
||||
res := map[string]interface{}{}
|
||||
for _, k := range keys {
|
||||
if v, ok := dict[k]; ok {
|
||||
res[k] = v
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func omit(dict map[string]interface{}, keys ...string) map[string]interface{} {
|
||||
res := map[string]interface{}{}
|
||||
|
||||
omit := make(map[string]bool, len(keys))
|
||||
for _, k := range keys {
|
||||
omit[k] = true
|
||||
}
|
||||
|
||||
for k, v := range dict {
|
||||
if _, ok := omit[k]; !ok {
|
||||
res[k] = v
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func dict(v ...interface{}) map[string]interface{} {
|
||||
dict := map[string]interface{}{}
|
||||
lenv := len(v)
|
||||
for i := 0; i < lenv; i += 2 {
|
||||
key := strval(v[i])
|
||||
if i+1 >= lenv {
|
||||
dict[key] = ""
|
||||
continue
|
||||
}
|
||||
dict[key] = v[i+1]
|
||||
}
|
||||
return dict
|
||||
}
|
||||
|
||||
func values(dict map[string]interface{}) []interface{} {
|
||||
values := []interface{}{}
|
||||
for _, value := range dict {
|
||||
values = append(values, value)
|
||||
}
|
||||
|
||||
return values
|
||||
}
|
||||
|
||||
func dig(ps ...interface{}) (interface{}, error) {
|
||||
if len(ps) < 3 {
|
||||
panic("dig needs at least three arguments")
|
||||
}
|
||||
dict := ps[len(ps)-1].(map[string]interface{})
|
||||
def := ps[len(ps)-2]
|
||||
ks := make([]string, len(ps)-2)
|
||||
for i := 0; i < len(ks); i++ {
|
||||
ks[i] = ps[i].(string)
|
||||
}
|
||||
|
||||
return digFromDict(dict, def, ks)
|
||||
}
|
||||
|
||||
func digFromDict(dict map[string]interface{}, d interface{}, ks []string) (interface{}, error) {
|
||||
k, ns := ks[0], ks[1:len(ks)]
|
||||
step, has := dict[k]
|
||||
if !has {
|
||||
return d, nil
|
||||
}
|
||||
if len(ns) == 0 {
|
||||
return step, nil
|
||||
}
|
||||
return digFromDict(step.(map[string]interface{}), d, ns)
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
Package sprig provides template functions for Go.
|
||||
|
||||
This package contains a number of utility functions for working with data
|
||||
inside of Go `html/template` and `text/template` files.
|
||||
|
||||
To add these functions, use the `template.Funcs()` method:
|
||||
|
||||
t := templates.New("foo").Funcs(sprig.FuncMap())
|
||||
|
||||
Note that you should add the function map before you parse any template files.
|
||||
|
||||
In several cases, Sprig reverses the order of arguments from the way they
|
||||
appear in the standard library. This is to make it easier to pipe
|
||||
arguments into functions.
|
||||
|
||||
See http://masterminds.github.io/sprig/ for more detailed documentation on each of the available functions.
|
||||
*/
|
||||
package sprig
|
|
@ -0,0 +1,317 @@
|
|||
package sprig
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"html/template"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
ttemplate "text/template"
|
||||
"time"
|
||||
)
|
||||
|
||||
// FuncMap produces the function map.
|
||||
//
|
||||
// Use this to pass the functions into the template engine:
|
||||
//
|
||||
// tpl := template.New("foo").Funcs(sprig.FuncMap()))
|
||||
//
|
||||
func FuncMap() template.FuncMap {
|
||||
return HtmlFuncMap()
|
||||
}
|
||||
|
||||
// HermeticTxtFuncMap returns a 'text/template'.FuncMap with only repeatable functions.
|
||||
func HermeticTxtFuncMap() ttemplate.FuncMap {
|
||||
r := TxtFuncMap()
|
||||
for _, name := range nonhermeticFunctions {
|
||||
delete(r, name)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// HermeticHtmlFuncMap returns an 'html/template'.Funcmap with only repeatable functions.
|
||||
func HermeticHtmlFuncMap() template.FuncMap {
|
||||
r := HtmlFuncMap()
|
||||
for _, name := range nonhermeticFunctions {
|
||||
delete(r, name)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// TxtFuncMap returns a 'text/template'.FuncMap
|
||||
func TxtFuncMap() ttemplate.FuncMap {
|
||||
return ttemplate.FuncMap(GenericFuncMap())
|
||||
}
|
||||
|
||||
// HtmlFuncMap returns an 'html/template'.Funcmap
|
||||
func HtmlFuncMap() template.FuncMap {
|
||||
return template.FuncMap(GenericFuncMap())
|
||||
}
|
||||
|
||||
// GenericFuncMap returns a copy of the basic function map as a map[string]interface{}.
|
||||
func GenericFuncMap() map[string]interface{} {
|
||||
gfm := make(map[string]interface{}, len(genericMap))
|
||||
for k, v := range genericMap {
|
||||
gfm[k] = v
|
||||
}
|
||||
return gfm
|
||||
}
|
||||
|
||||
// These functions are not guaranteed to evaluate to the same result for given input, because they
|
||||
// refer to the environment or global state.
|
||||
var nonhermeticFunctions = []string{
|
||||
// Date functions
|
||||
"date",
|
||||
"date_in_zone",
|
||||
"date_modify",
|
||||
"now",
|
||||
"htmlDate",
|
||||
"htmlDateInZone",
|
||||
"dateInZone",
|
||||
"dateModify",
|
||||
|
||||
// Strings
|
||||
"randAlphaNum",
|
||||
"randAlpha",
|
||||
"randAscii",
|
||||
"randNumeric",
|
||||
"randBytes",
|
||||
"uuidv4",
|
||||
|
||||
// OS
|
||||
"env",
|
||||
"expandenv",
|
||||
|
||||
// Network
|
||||
"getHostByName",
|
||||
}
|
||||
|
||||
var genericMap = map[string]interface{}{
|
||||
"hello": func() string { return "Hello!" },
|
||||
|
||||
// Date functions
|
||||
"ago": dateAgo,
|
||||
"date": date,
|
||||
"date_in_zone": dateInZone,
|
||||
"date_modify": dateModify,
|
||||
"dateInZone": dateInZone,
|
||||
"dateModify": dateModify,
|
||||
"duration": duration,
|
||||
"durationRound": durationRound,
|
||||
"htmlDate": htmlDate,
|
||||
"htmlDateInZone": htmlDateInZone,
|
||||
"must_date_modify": mustDateModify,
|
||||
"mustDateModify": mustDateModify,
|
||||
"mustToDate": mustToDate,
|
||||
"now": time.Now,
|
||||
"toDate": toDate,
|
||||
"unixEpoch": unixEpoch,
|
||||
|
||||
// Strings
|
||||
"trunc": trunc,
|
||||
"trim": strings.TrimSpace,
|
||||
"upper": strings.ToUpper,
|
||||
"lower": strings.ToLower,
|
||||
"title": strings.Title,
|
||||
"substr": substring,
|
||||
// Switch order so that "foo" | repeat 5
|
||||
"repeat": func(count int, str string) string { return strings.Repeat(str, count) },
|
||||
// Deprecated: Use trimAll.
|
||||
"trimall": func(a, b string) string { return strings.Trim(b, a) },
|
||||
// Switch order so that "$foo" | trimall "$"
|
||||
"trimAll": func(a, b string) string { return strings.Trim(b, a) },
|
||||
"trimSuffix": func(a, b string) string { return strings.TrimSuffix(b, a) },
|
||||
"trimPrefix": func(a, b string) string { return strings.TrimPrefix(b, a) },
|
||||
// Switch order so that "foobar" | contains "foo"
|
||||
"contains": func(substr string, str string) bool { return strings.Contains(str, substr) },
|
||||
"hasPrefix": func(substr string, str string) bool { return strings.HasPrefix(str, substr) },
|
||||
"hasSuffix": func(substr string, str string) bool { return strings.HasSuffix(str, substr) },
|
||||
"quote": quote,
|
||||
"squote": squote,
|
||||
"cat": cat,
|
||||
"indent": indent,
|
||||
"nindent": nindent,
|
||||
"replace": replace,
|
||||
"plural": plural,
|
||||
"sha1sum": sha1sum,
|
||||
"sha256sum": sha256sum,
|
||||
"adler32sum": adler32sum,
|
||||
"toString": strval,
|
||||
|
||||
// Wrap Atoi to stop errors.
|
||||
"atoi": func(a string) int { i, _ := strconv.Atoi(a); return i },
|
||||
"int64": toInt64,
|
||||
"int": toInt,
|
||||
"float64": toFloat64,
|
||||
"seq": seq,
|
||||
"toDecimal": toDecimal,
|
||||
|
||||
//"gt": func(a, b int) bool {return a > b},
|
||||
//"gte": func(a, b int) bool {return a >= b},
|
||||
//"lt": func(a, b int) bool {return a < b},
|
||||
//"lte": func(a, b int) bool {return a <= b},
|
||||
|
||||
// split "/" foo/bar returns map[int]string{0: foo, 1: bar}
|
||||
"split": split,
|
||||
"splitList": func(sep, orig string) []string { return strings.Split(orig, sep) },
|
||||
// splitn "/" foo/bar/fuu returns map[int]string{0: foo, 1: bar/fuu}
|
||||
"splitn": splitn,
|
||||
"toStrings": strslice,
|
||||
|
||||
"until": until,
|
||||
"untilStep": untilStep,
|
||||
|
||||
// VERY basic arithmetic.
|
||||
"add1": func(i interface{}) int64 { return toInt64(i) + 1 },
|
||||
"add": func(i ...interface{}) int64 {
|
||||
var a int64 = 0
|
||||
for _, b := range i {
|
||||
a += toInt64(b)
|
||||
}
|
||||
return a
|
||||
},
|
||||
"sub": func(a, b interface{}) int64 { return toInt64(a) - toInt64(b) },
|
||||
"div": func(a, b interface{}) int64 { return toInt64(a) / toInt64(b) },
|
||||
"mod": func(a, b interface{}) int64 { return toInt64(a) % toInt64(b) },
|
||||
"mul": func(a interface{}, v ...interface{}) int64 {
|
||||
val := toInt64(a)
|
||||
for _, b := range v {
|
||||
val = val * toInt64(b)
|
||||
}
|
||||
return val
|
||||
},
|
||||
"randInt": func(min, max int) int { return rand.Intn(max-min) + min },
|
||||
"biggest": max,
|
||||
"max": max,
|
||||
"min": min,
|
||||
"maxf": maxf,
|
||||
"minf": minf,
|
||||
"ceil": ceil,
|
||||
"floor": floor,
|
||||
"round": round,
|
||||
|
||||
// string slices. Note that we reverse the order b/c that's better
|
||||
// for template processing.
|
||||
"join": join,
|
||||
"sortAlpha": sortAlpha,
|
||||
|
||||
// Defaults
|
||||
"default": dfault,
|
||||
"empty": empty,
|
||||
"coalesce": coalesce,
|
||||
"all": all,
|
||||
"any": any,
|
||||
"compact": compact,
|
||||
"mustCompact": mustCompact,
|
||||
"fromJson": fromJson,
|
||||
"toJson": toJson,
|
||||
"toPrettyJson": toPrettyJson,
|
||||
"toRawJson": toRawJson,
|
||||
"mustFromJson": mustFromJson,
|
||||
"mustToJson": mustToJson,
|
||||
"mustToPrettyJson": mustToPrettyJson,
|
||||
"mustToRawJson": mustToRawJson,
|
||||
"ternary": ternary,
|
||||
|
||||
// Reflection
|
||||
"typeOf": typeOf,
|
||||
"typeIs": typeIs,
|
||||
"typeIsLike": typeIsLike,
|
||||
"kindOf": kindOf,
|
||||
"kindIs": kindIs,
|
||||
"deepEqual": reflect.DeepEqual,
|
||||
|
||||
// OS:
|
||||
"env": os.Getenv,
|
||||
"expandenv": os.ExpandEnv,
|
||||
|
||||
// Network:
|
||||
"getHostByName": getHostByName,
|
||||
|
||||
// Paths:
|
||||
"base": path.Base,
|
||||
"dir": path.Dir,
|
||||
"clean": path.Clean,
|
||||
"ext": path.Ext,
|
||||
"isAbs": path.IsAbs,
|
||||
|
||||
// Filepaths:
|
||||
"osBase": filepath.Base,
|
||||
"osClean": filepath.Clean,
|
||||
"osDir": filepath.Dir,
|
||||
"osExt": filepath.Ext,
|
||||
"osIsAbs": filepath.IsAbs,
|
||||
|
||||
// Encoding:
|
||||
"b64enc": base64encode,
|
||||
"b64dec": base64decode,
|
||||
"b32enc": base32encode,
|
||||
"b32dec": base32decode,
|
||||
|
||||
// Data Structures:
|
||||
"tuple": list, // FIXME: with the addition of append/prepend these are no longer immutable.
|
||||
"list": list,
|
||||
"dict": dict,
|
||||
"get": get,
|
||||
"set": set,
|
||||
"unset": unset,
|
||||
"hasKey": hasKey,
|
||||
"pluck": pluck,
|
||||
"keys": keys,
|
||||
"pick": pick,
|
||||
"omit": omit,
|
||||
"values": values,
|
||||
|
||||
"append": push, "push": push,
|
||||
"mustAppend": mustPush, "mustPush": mustPush,
|
||||
"prepend": prepend,
|
||||
"mustPrepend": mustPrepend,
|
||||
"first": first,
|
||||
"mustFirst": mustFirst,
|
||||
"rest": rest,
|
||||
"mustRest": mustRest,
|
||||
"last": last,
|
||||
"mustLast": mustLast,
|
||||
"initial": initial,
|
||||
"mustInitial": mustInitial,
|
||||
"reverse": reverse,
|
||||
"mustReverse": mustReverse,
|
||||
"uniq": uniq,
|
||||
"mustUniq": mustUniq,
|
||||
"without": without,
|
||||
"mustWithout": mustWithout,
|
||||
"has": has,
|
||||
"mustHas": mustHas,
|
||||
"slice": slice,
|
||||
"mustSlice": mustSlice,
|
||||
"concat": concat,
|
||||
"dig": dig,
|
||||
"chunk": chunk,
|
||||
"mustChunk": mustChunk,
|
||||
|
||||
// Flow Control:
|
||||
"fail": func(msg string) (string, error) { return "", errors.New(msg) },
|
||||
|
||||
// Regex
|
||||
"regexMatch": regexMatch,
|
||||
"mustRegexMatch": mustRegexMatch,
|
||||
"regexFindAll": regexFindAll,
|
||||
"mustRegexFindAll": mustRegexFindAll,
|
||||
"regexFind": regexFind,
|
||||
"mustRegexFind": mustRegexFind,
|
||||
"regexReplaceAll": regexReplaceAll,
|
||||
"mustRegexReplaceAll": mustRegexReplaceAll,
|
||||
"regexReplaceAllLiteral": regexReplaceAllLiteral,
|
||||
"mustRegexReplaceAllLiteral": mustRegexReplaceAllLiteral,
|
||||
"regexSplit": regexSplit,
|
||||
"mustRegexSplit": mustRegexSplit,
|
||||
"regexQuoteMeta": regexQuoteMeta,
|
||||
|
||||
// URLs:
|
||||
"urlParse": urlParse,
|
||||
"urlJoin": urlJoin,
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
module github.com/go-task/slim-sprig
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/stretchr/testify v1.5.1
|
||||
)
|
|
@ -0,0 +1,22 @@
|
|||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
@ -0,0 +1,464 @@
|
|||
package sprig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Reflection is used in these functions so that slices and arrays of strings,
|
||||
// ints, and other types not implementing []interface{} can be worked with.
|
||||
// For example, this is useful if you need to work on the output of regexs.
|
||||
|
||||
func list(v ...interface{}) []interface{} {
|
||||
return v
|
||||
}
|
||||
|
||||
func push(list interface{}, v interface{}) []interface{} {
|
||||
l, err := mustPush(list, v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func mustPush(list interface{}, v interface{}) ([]interface{}, error) {
|
||||
tp := reflect.TypeOf(list).Kind()
|
||||
switch tp {
|
||||
case reflect.Slice, reflect.Array:
|
||||
l2 := reflect.ValueOf(list)
|
||||
|
||||
l := l2.Len()
|
||||
nl := make([]interface{}, l)
|
||||
for i := 0; i < l; i++ {
|
||||
nl[i] = l2.Index(i).Interface()
|
||||
}
|
||||
|
||||
return append(nl, v), nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("Cannot push on type %s", tp)
|
||||
}
|
||||
}
|
||||
|
||||
func prepend(list interface{}, v interface{}) []interface{} {
|
||||
l, err := mustPrepend(list, v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func mustPrepend(list interface{}, v interface{}) ([]interface{}, error) {
|
||||
//return append([]interface{}{v}, list...)
|
||||
|
||||
tp := reflect.TypeOf(list).Kind()
|
||||
switch tp {
|
||||
case reflect.Slice, reflect.Array:
|
||||
l2 := reflect.ValueOf(list)
|
||||
|
||||
l := l2.Len()
|
||||
nl := make([]interface{}, l)
|
||||
for i := 0; i < l; i++ {
|
||||
nl[i] = l2.Index(i).Interface()
|
||||
}
|
||||
|
||||
return append([]interface{}{v}, nl...), nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("Cannot prepend on type %s", tp)
|
||||
}
|
||||
}
|
||||
|
||||
func chunk(size int, list interface{}) [][]interface{} {
|
||||
l, err := mustChunk(size, list)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func mustChunk(size int, list interface{}) ([][]interface{}, error) {
|
||||
tp := reflect.TypeOf(list).Kind()
|
||||
switch tp {
|
||||
case reflect.Slice, reflect.Array:
|
||||
l2 := reflect.ValueOf(list)
|
||||
|
||||
l := l2.Len()
|
||||
|
||||
cs := int(math.Floor(float64(l-1)/float64(size)) + 1)
|
||||
nl := make([][]interface{}, cs)
|
||||
|
||||
for i := 0; i < cs; i++ {
|
||||
clen := size
|
||||
if i == cs-1 {
|
||||
clen = int(math.Floor(math.Mod(float64(l), float64(size))))
|
||||
if clen == 0 {
|
||||
clen = size
|
||||
}
|
||||
}
|
||||
|
||||
nl[i] = make([]interface{}, clen)
|
||||
|
||||
for j := 0; j < clen; j++ {
|
||||
ix := i*size + j
|
||||
nl[i][j] = l2.Index(ix).Interface()
|
||||
}
|
||||
}
|
||||
|
||||
return nl, nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("Cannot chunk type %s", tp)
|
||||
}
|
||||
}
|
||||
|
||||
func last(list interface{}) interface{} {
|
||||
l, err := mustLast(list)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func mustLast(list interface{}) (interface{}, error) {
|
||||
tp := reflect.TypeOf(list).Kind()
|
||||
switch tp {
|
||||
case reflect.Slice, reflect.Array:
|
||||
l2 := reflect.ValueOf(list)
|
||||
|
||||
l := l2.Len()
|
||||
if l == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return l2.Index(l - 1).Interface(), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("Cannot find last on type %s", tp)
|
||||
}
|
||||
}
|
||||
|
||||
func first(list interface{}) interface{} {
|
||||
l, err := mustFirst(list)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func mustFirst(list interface{}) (interface{}, error) {
|
||||
tp := reflect.TypeOf(list).Kind()
|
||||
switch tp {
|
||||
case reflect.Slice, reflect.Array:
|
||||
l2 := reflect.ValueOf(list)
|
||||
|
||||
l := l2.Len()
|
||||
if l == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return l2.Index(0).Interface(), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("Cannot find first on type %s", tp)
|
||||
}
|
||||
}
|
||||
|
||||
func rest(list interface{}) []interface{} {
|
||||
l, err := mustRest(list)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func mustRest(list interface{}) ([]interface{}, error) {
|
||||
tp := reflect.TypeOf(list).Kind()
|
||||
switch tp {
|
||||
case reflect.Slice, reflect.Array:
|
||||
l2 := reflect.ValueOf(list)
|
||||
|
||||
l := l2.Len()
|
||||
if l == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
nl := make([]interface{}, l-1)
|
||||
for i := 1; i < l; i++ {
|
||||
nl[i-1] = l2.Index(i).Interface()
|
||||
}
|
||||
|
||||
return nl, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("Cannot find rest on type %s", tp)
|
||||
}
|
||||
}
|
||||
|
||||
func initial(list interface{}) []interface{} {
|
||||
l, err := mustInitial(list)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func mustInitial(list interface{}) ([]interface{}, error) {
|
||||
tp := reflect.TypeOf(list).Kind()
|
||||
switch tp {
|
||||
case reflect.Slice, reflect.Array:
|
||||
l2 := reflect.ValueOf(list)
|
||||
|
||||
l := l2.Len()
|
||||
if l == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
nl := make([]interface{}, l-1)
|
||||
for i := 0; i < l-1; i++ {
|
||||
nl[i] = l2.Index(i).Interface()
|
||||
}
|
||||
|
||||
return nl, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("Cannot find initial on type %s", tp)
|
||||
}
|
||||
}
|
||||
|
||||
func sortAlpha(list interface{}) []string {
|
||||
k := reflect.Indirect(reflect.ValueOf(list)).Kind()
|
||||
switch k {
|
||||
case reflect.Slice, reflect.Array:
|
||||
a := strslice(list)
|
||||
s := sort.StringSlice(a)
|
||||
s.Sort()
|
||||
return s
|
||||
}
|
||||
return []string{strval(list)}
|
||||
}
|
||||
|
||||
func reverse(v interface{}) []interface{} {
|
||||
l, err := mustReverse(v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func mustReverse(v interface{}) ([]interface{}, error) {
|
||||
tp := reflect.TypeOf(v).Kind()
|
||||
switch tp {
|
||||
case reflect.Slice, reflect.Array:
|
||||
l2 := reflect.ValueOf(v)
|
||||
|
||||
l := l2.Len()
|
||||
// We do not sort in place because the incoming array should not be altered.
|
||||
nl := make([]interface{}, l)
|
||||
for i := 0; i < l; i++ {
|
||||
nl[l-i-1] = l2.Index(i).Interface()
|
||||
}
|
||||
|
||||
return nl, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("Cannot find reverse on type %s", tp)
|
||||
}
|
||||
}
|
||||
|
||||
func compact(list interface{}) []interface{} {
|
||||
l, err := mustCompact(list)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func mustCompact(list interface{}) ([]interface{}, error) {
|
||||
tp := reflect.TypeOf(list).Kind()
|
||||
switch tp {
|
||||
case reflect.Slice, reflect.Array:
|
||||
l2 := reflect.ValueOf(list)
|
||||
|
||||
l := l2.Len()
|
||||
nl := []interface{}{}
|
||||
var item interface{}
|
||||
for i := 0; i < l; i++ {
|
||||
item = l2.Index(i).Interface()
|
||||
if !empty(item) {
|
||||
nl = append(nl, item)
|
||||
}
|
||||
}
|
||||
|
||||
return nl, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("Cannot compact on type %s", tp)
|
||||
}
|
||||
}
|
||||
|
||||
func uniq(list interface{}) []interface{} {
|
||||
l, err := mustUniq(list)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func mustUniq(list interface{}) ([]interface{}, error) {
|
||||
tp := reflect.TypeOf(list).Kind()
|
||||
switch tp {
|
||||
case reflect.Slice, reflect.Array:
|
||||
l2 := reflect.ValueOf(list)
|
||||
|
||||
l := l2.Len()
|
||||
dest := []interface{}{}
|
||||
var item interface{}
|
||||
for i := 0; i < l; i++ {
|
||||
item = l2.Index(i).Interface()
|
||||
if !inList(dest, item) {
|
||||
dest = append(dest, item)
|
||||
}
|
||||
}
|
||||
|
||||
return dest, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("Cannot find uniq on type %s", tp)
|
||||
}
|
||||
}
|
||||
|
||||
func inList(haystack []interface{}, needle interface{}) bool {
|
||||
for _, h := range haystack {
|
||||
if reflect.DeepEqual(needle, h) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func without(list interface{}, omit ...interface{}) []interface{} {
|
||||
l, err := mustWithout(list, omit...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func mustWithout(list interface{}, omit ...interface{}) ([]interface{}, error) {
|
||||
tp := reflect.TypeOf(list).Kind()
|
||||
switch tp {
|
||||
case reflect.Slice, reflect.Array:
|
||||
l2 := reflect.ValueOf(list)
|
||||
|
||||
l := l2.Len()
|
||||
res := []interface{}{}
|
||||
var item interface{}
|
||||
for i := 0; i < l; i++ {
|
||||
item = l2.Index(i).Interface()
|
||||
if !inList(omit, item) {
|
||||
res = append(res, item)
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("Cannot find without on type %s", tp)
|
||||
}
|
||||
}
|
||||
|
||||
func has(needle interface{}, haystack interface{}) bool {
|
||||
l, err := mustHas(needle, haystack)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func mustHas(needle interface{}, haystack interface{}) (bool, error) {
|
||||
if haystack == nil {
|
||||
return false, nil
|
||||
}
|
||||
tp := reflect.TypeOf(haystack).Kind()
|
||||
switch tp {
|
||||
case reflect.Slice, reflect.Array:
|
||||
l2 := reflect.ValueOf(haystack)
|
||||
var item interface{}
|
||||
l := l2.Len()
|
||||
for i := 0; i < l; i++ {
|
||||
item = l2.Index(i).Interface()
|
||||
if reflect.DeepEqual(needle, item) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("Cannot find has on type %s", tp)
|
||||
}
|
||||
}
|
||||
|
||||
// $list := [1, 2, 3, 4, 5]
|
||||
// slice $list -> list[0:5] = list[:]
|
||||
// slice $list 0 3 -> list[0:3] = list[:3]
|
||||
// slice $list 3 5 -> list[3:5]
|
||||
// slice $list 3 -> list[3:5] = list[3:]
|
||||
func slice(list interface{}, indices ...interface{}) interface{} {
|
||||
l, err := mustSlice(list, indices...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func mustSlice(list interface{}, indices ...interface{}) (interface{}, error) {
|
||||
tp := reflect.TypeOf(list).Kind()
|
||||
switch tp {
|
||||
case reflect.Slice, reflect.Array:
|
||||
l2 := reflect.ValueOf(list)
|
||||
|
||||
l := l2.Len()
|
||||
if l == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var start, end int
|
||||
if len(indices) > 0 {
|
||||
start = toInt(indices[0])
|
||||
}
|
||||
if len(indices) < 2 {
|
||||
end = l
|
||||
} else {
|
||||
end = toInt(indices[1])
|
||||
}
|
||||
|
||||
return l2.Slice(start, end).Interface(), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("list should be type of slice or array but %s", tp)
|
||||
}
|
||||
}
|
||||
|
||||
func concat(lists ...interface{}) interface{} {
|
||||
var res []interface{}
|
||||
for _, list := range lists {
|
||||
tp := reflect.TypeOf(list).Kind()
|
||||
switch tp {
|
||||
case reflect.Slice, reflect.Array:
|
||||
l2 := reflect.ValueOf(list)
|
||||
for i := 0; i < l2.Len(); i++ {
|
||||
res = append(res, l2.Index(i).Interface())
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("Cannot concat type %s as list", tp))
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package sprig
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"net"
|
||||
)
|
||||
|
||||
func getHostByName(name string) string {
|
||||
addrs, _ := net.LookupHost(name)
|
||||
//TODO: add error handing when release v3 comes out
|
||||
return addrs[rand.Intn(len(addrs))]
|
||||
}
|
|
@ -0,0 +1,228 @@
|
|||
package sprig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// toFloat64 converts 64-bit floats
|
||||
func toFloat64(v interface{}) float64 {
|
||||
if str, ok := v.(string); ok {
|
||||
iv, err := strconv.ParseFloat(str, 64)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return iv
|
||||
}
|
||||
|
||||
val := reflect.Indirect(reflect.ValueOf(v))
|
||||
switch val.Kind() {
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||
return float64(val.Int())
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32:
|
||||
return float64(val.Uint())
|
||||
case reflect.Uint, reflect.Uint64:
|
||||
return float64(val.Uint())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return val.Float()
|
||||
case reflect.Bool:
|
||||
if val.Bool() {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func toInt(v interface{}) int {
|
||||
//It's not optimal. Bud I don't want duplicate toInt64 code.
|
||||
return int(toInt64(v))
|
||||
}
|
||||
|
||||
// toInt64 converts integer types to 64-bit integers
|
||||
func toInt64(v interface{}) int64 {
|
||||
if str, ok := v.(string); ok {
|
||||
iv, err := strconv.ParseInt(str, 10, 64)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return iv
|
||||
}
|
||||
|
||||
val := reflect.Indirect(reflect.ValueOf(v))
|
||||
switch val.Kind() {
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||
return val.Int()
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32:
|
||||
return int64(val.Uint())
|
||||
case reflect.Uint, reflect.Uint64:
|
||||
tv := val.Uint()
|
||||
if tv <= math.MaxInt64 {
|
||||
return int64(tv)
|
||||
}
|
||||
// TODO: What is the sensible thing to do here?
|
||||
return math.MaxInt64
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return int64(val.Float())
|
||||
case reflect.Bool:
|
||||
if val.Bool() {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func max(a interface{}, i ...interface{}) int64 {
|
||||
aa := toInt64(a)
|
||||
for _, b := range i {
|
||||
bb := toInt64(b)
|
||||
if bb > aa {
|
||||
aa = bb
|
||||
}
|
||||
}
|
||||
return aa
|
||||
}
|
||||
|
||||
func maxf(a interface{}, i ...interface{}) float64 {
|
||||
aa := toFloat64(a)
|
||||
for _, b := range i {
|
||||
bb := toFloat64(b)
|
||||
aa = math.Max(aa, bb)
|
||||
}
|
||||
return aa
|
||||
}
|
||||
|
||||
func min(a interface{}, i ...interface{}) int64 {
|
||||
aa := toInt64(a)
|
||||
for _, b := range i {
|
||||
bb := toInt64(b)
|
||||
if bb < aa {
|
||||
aa = bb
|
||||
}
|
||||
}
|
||||
return aa
|
||||
}
|
||||
|
||||
func minf(a interface{}, i ...interface{}) float64 {
|
||||
aa := toFloat64(a)
|
||||
for _, b := range i {
|
||||
bb := toFloat64(b)
|
||||
aa = math.Min(aa, bb)
|
||||
}
|
||||
return aa
|
||||
}
|
||||
|
||||
func until(count int) []int {
|
||||
step := 1
|
||||
if count < 0 {
|
||||
step = -1
|
||||
}
|
||||
return untilStep(0, count, step)
|
||||
}
|
||||
|
||||
func untilStep(start, stop, step int) []int {
|
||||
v := []int{}
|
||||
|
||||
if stop < start {
|
||||
if step >= 0 {
|
||||
return v
|
||||
}
|
||||
for i := start; i > stop; i += step {
|
||||
v = append(v, i)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
if step <= 0 {
|
||||
return v
|
||||
}
|
||||
for i := start; i < stop; i += step {
|
||||
v = append(v, i)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func floor(a interface{}) float64 {
|
||||
aa := toFloat64(a)
|
||||
return math.Floor(aa)
|
||||
}
|
||||
|
||||
func ceil(a interface{}) float64 {
|
||||
aa := toFloat64(a)
|
||||
return math.Ceil(aa)
|
||||
}
|
||||
|
||||
func round(a interface{}, p int, rOpt ...float64) float64 {
|
||||
roundOn := .5
|
||||
if len(rOpt) > 0 {
|
||||
roundOn = rOpt[0]
|
||||
}
|
||||
val := toFloat64(a)
|
||||
places := toFloat64(p)
|
||||
|
||||
var round float64
|
||||
pow := math.Pow(10, places)
|
||||
digit := pow * val
|
||||
_, div := math.Modf(digit)
|
||||
if div >= roundOn {
|
||||
round = math.Ceil(digit)
|
||||
} else {
|
||||
round = math.Floor(digit)
|
||||
}
|
||||
return round / pow
|
||||
}
|
||||
|
||||
// converts unix octal to decimal
|
||||
func toDecimal(v interface{}) int64 {
|
||||
result, err := strconv.ParseInt(fmt.Sprint(v), 8, 64)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func seq(params ...int) string {
|
||||
increment := 1
|
||||
switch len(params) {
|
||||
case 0:
|
||||
return ""
|
||||
case 1:
|
||||
start := 1
|
||||
end := params[0]
|
||||
if end < start {
|
||||
increment = -1
|
||||
}
|
||||
return intArrayToString(untilStep(start, end+increment, increment), " ")
|
||||
case 3:
|
||||
start := params[0]
|
||||
end := params[2]
|
||||
step := params[1]
|
||||
if end < start {
|
||||
increment = -1
|
||||
if step > 0 {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return intArrayToString(untilStep(start, end+increment, step), " ")
|
||||
case 2:
|
||||
start := params[0]
|
||||
end := params[1]
|
||||
step := 1
|
||||
if end < start {
|
||||
step = -1
|
||||
}
|
||||
return intArrayToString(untilStep(start, end+step, step), " ")
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func intArrayToString(slice []int, delimeter string) string {
|
||||
return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(slice)), delimeter), "[]")
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package sprig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// typeIs returns true if the src is the type named in target.
|
||||
func typeIs(target string, src interface{}) bool {
|
||||
return target == typeOf(src)
|
||||
}
|
||||
|
||||
func typeIsLike(target string, src interface{}) bool {
|
||||
t := typeOf(src)
|
||||
return target == t || "*"+target == t
|
||||
}
|
||||
|
||||
func typeOf(src interface{}) string {
|
||||
return fmt.Sprintf("%T", src)
|
||||
}
|
||||
|
||||
func kindIs(target string, src interface{}) bool {
|
||||
return target == kindOf(src)
|
||||
}
|
||||
|
||||
func kindOf(src interface{}) string {
|
||||
return reflect.ValueOf(src).Kind().String()
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package sprig
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
)
|
||||
|
||||
func regexMatch(regex string, s string) bool {
|
||||
match, _ := regexp.MatchString(regex, s)
|
||||
return match
|
||||
}
|
||||
|
||||
func mustRegexMatch(regex string, s string) (bool, error) {
|
||||
return regexp.MatchString(regex, s)
|
||||
}
|
||||
|
||||
func regexFindAll(regex string, s string, n int) []string {
|
||||
r := regexp.MustCompile(regex)
|
||||
return r.FindAllString(s, n)
|
||||
}
|
||||
|
||||
func mustRegexFindAll(regex string, s string, n int) ([]string, error) {
|
||||
r, err := regexp.Compile(regex)
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
return r.FindAllString(s, n), nil
|
||||
}
|
||||
|
||||
func regexFind(regex string, s string) string {
|
||||
r := regexp.MustCompile(regex)
|
||||
return r.FindString(s)
|
||||
}
|
||||
|
||||
func mustRegexFind(regex string, s string) (string, error) {
|
||||
r, err := regexp.Compile(regex)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return r.FindString(s), nil
|
||||
}
|
||||
|
||||
func regexReplaceAll(regex string, s string, repl string) string {
|
||||
r := regexp.MustCompile(regex)
|
||||
return r.ReplaceAllString(s, repl)
|
||||
}
|
||||
|
||||
func mustRegexReplaceAll(regex string, s string, repl string) (string, error) {
|
||||
r, err := regexp.Compile(regex)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return r.ReplaceAllString(s, repl), nil
|
||||
}
|
||||
|
||||
func regexReplaceAllLiteral(regex string, s string, repl string) string {
|
||||
r := regexp.MustCompile(regex)
|
||||
return r.ReplaceAllLiteralString(s, repl)
|
||||
}
|
||||
|
||||
func mustRegexReplaceAllLiteral(regex string, s string, repl string) (string, error) {
|
||||
r, err := regexp.Compile(regex)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return r.ReplaceAllLiteralString(s, repl), nil
|
||||
}
|
||||
|
||||
func regexSplit(regex string, s string, n int) []string {
|
||||
r := regexp.MustCompile(regex)
|
||||
return r.Split(s, n)
|
||||
}
|
||||
|
||||
func mustRegexSplit(regex string, s string, n int) ([]string, error) {
|
||||
r, err := regexp.Compile(regex)
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
return r.Split(s, n), nil
|
||||
}
|
||||
|
||||
func regexQuoteMeta(s string) string {
|
||||
return regexp.QuoteMeta(s)
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
package sprig
|
||||
|
||||
import (
|
||||
"encoding/base32"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func base64encode(v string) string {
|
||||
return base64.StdEncoding.EncodeToString([]byte(v))
|
||||
}
|
||||
|
||||
func base64decode(v string) string {
|
||||
data, err := base64.StdEncoding.DecodeString(v)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
|
||||
func base32encode(v string) string {
|
||||
return base32.StdEncoding.EncodeToString([]byte(v))
|
||||
}
|
||||
|
||||
func base32decode(v string) string {
|
||||
data, err := base32.StdEncoding.DecodeString(v)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
|
||||
func quote(str ...interface{}) string {
|
||||
out := make([]string, 0, len(str))
|
||||
for _, s := range str {
|
||||
if s != nil {
|
||||
out = append(out, fmt.Sprintf("%q", strval(s)))
|
||||
}
|
||||
}
|
||||
return strings.Join(out, " ")
|
||||
}
|
||||
|
||||
func squote(str ...interface{}) string {
|
||||
out := make([]string, 0, len(str))
|
||||
for _, s := range str {
|
||||
if s != nil {
|
||||
out = append(out, fmt.Sprintf("'%v'", s))
|
||||
}
|
||||
}
|
||||
return strings.Join(out, " ")
|
||||
}
|
||||
|
||||
func cat(v ...interface{}) string {
|
||||
v = removeNilElements(v)
|
||||
r := strings.TrimSpace(strings.Repeat("%v ", len(v)))
|
||||
return fmt.Sprintf(r, v...)
|
||||
}
|
||||
|
||||
func indent(spaces int, v string) string {
|
||||
pad := strings.Repeat(" ", spaces)
|
||||
return pad + strings.Replace(v, "\n", "\n"+pad, -1)
|
||||
}
|
||||
|
||||
func nindent(spaces int, v string) string {
|
||||
return "\n" + indent(spaces, v)
|
||||
}
|
||||
|
||||
func replace(old, new, src string) string {
|
||||
return strings.Replace(src, old, new, -1)
|
||||
}
|
||||
|
||||
func plural(one, many string, count int) string {
|
||||
if count == 1 {
|
||||
return one
|
||||
}
|
||||
return many
|
||||
}
|
||||
|
||||
func strslice(v interface{}) []string {
|
||||
switch v := v.(type) {
|
||||
case []string:
|
||||
return v
|
||||
case []interface{}:
|
||||
b := make([]string, 0, len(v))
|
||||
for _, s := range v {
|
||||
if s != nil {
|
||||
b = append(b, strval(s))
|
||||
}
|
||||
}
|
||||
return b
|
||||
default:
|
||||
val := reflect.ValueOf(v)
|
||||
switch val.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
l := val.Len()
|
||||
b := make([]string, 0, l)
|
||||
for i := 0; i < l; i++ {
|
||||
value := val.Index(i).Interface()
|
||||
if value != nil {
|
||||
b = append(b, strval(value))
|
||||
}
|
||||
}
|
||||
return b
|
||||
default:
|
||||
if v == nil {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
return []string{strval(v)}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func removeNilElements(v []interface{}) []interface{} {
|
||||
newSlice := make([]interface{}, 0, len(v))
|
||||
for _, i := range v {
|
||||
if i != nil {
|
||||
newSlice = append(newSlice, i)
|
||||
}
|
||||
}
|
||||
return newSlice
|
||||
}
|
||||
|
||||
func strval(v interface{}) string {
|
||||
switch v := v.(type) {
|
||||
case string:
|
||||
return v
|
||||
case []byte:
|
||||
return string(v)
|
||||
case error:
|
||||
return v.Error()
|
||||
case fmt.Stringer:
|
||||
return v.String()
|
||||
default:
|
||||
return fmt.Sprintf("%v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func trunc(c int, s string) string {
|
||||
if c < 0 && len(s)+c > 0 {
|
||||
return s[len(s)+c:]
|
||||
}
|
||||
if c >= 0 && len(s) > c {
|
||||
return s[:c]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func join(sep string, v interface{}) string {
|
||||
return strings.Join(strslice(v), sep)
|
||||
}
|
||||
|
||||
func split(sep, orig string) map[string]string {
|
||||
parts := strings.Split(orig, sep)
|
||||
res := make(map[string]string, len(parts))
|
||||
for i, v := range parts {
|
||||
res["_"+strconv.Itoa(i)] = v
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func splitn(sep string, n int, orig string) map[string]string {
|
||||
parts := strings.SplitN(orig, sep, n)
|
||||
res := make(map[string]string, len(parts))
|
||||
for i, v := range parts {
|
||||
res["_"+strconv.Itoa(i)] = v
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// substring creates a substring of the given string.
|
||||
//
|
||||
// If start is < 0, this calls string[:end].
|
||||
//
|
||||
// If start is >= 0 and end < 0 or end bigger than s length, this calls string[start:]
|
||||
//
|
||||
// Otherwise, this calls string[start, end].
|
||||
func substring(start, end int, s string) string {
|
||||
if start < 0 {
|
||||
return s[:end]
|
||||
}
|
||||
if end < 0 || end > len(s) {
|
||||
return s[start:]
|
||||
}
|
||||
return s[start:end]
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package sprig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func dictGetOrEmpty(dict map[string]interface{}, key string) string {
|
||||
value, ok := dict[key]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
tp := reflect.TypeOf(value).Kind()
|
||||
if tp != reflect.String {
|
||||
panic(fmt.Sprintf("unable to parse %s key, must be of type string, but %s found", key, tp.String()))
|
||||
}
|
||||
return reflect.ValueOf(value).String()
|
||||
}
|
||||
|
||||
// parses given URL to return dict object
|
||||
func urlParse(v string) map[string]interface{} {
|
||||
dict := map[string]interface{}{}
|
||||
parsedURL, err := url.Parse(v)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("unable to parse url: %s", err))
|
||||
}
|
||||
dict["scheme"] = parsedURL.Scheme
|
||||
dict["host"] = parsedURL.Host
|
||||
dict["hostname"] = parsedURL.Hostname()
|
||||
dict["path"] = parsedURL.Path
|
||||
dict["query"] = parsedURL.RawQuery
|
||||
dict["opaque"] = parsedURL.Opaque
|
||||
dict["fragment"] = parsedURL.Fragment
|
||||
if parsedURL.User != nil {
|
||||
dict["userinfo"] = parsedURL.User.String()
|
||||
} else {
|
||||
dict["userinfo"] = ""
|
||||
}
|
||||
|
||||
return dict
|
||||
}
|
||||
|
||||
// join given dict to URL string
|
||||
func urlJoin(d map[string]interface{}) string {
|
||||
resURL := url.URL{
|
||||
Scheme: dictGetOrEmpty(d, "scheme"),
|
||||
Host: dictGetOrEmpty(d, "host"),
|
||||
Path: dictGetOrEmpty(d, "path"),
|
||||
RawQuery: dictGetOrEmpty(d, "query"),
|
||||
Opaque: dictGetOrEmpty(d, "opaque"),
|
||||
Fragment: dictGetOrEmpty(d, "fragment"),
|
||||
}
|
||||
userinfo := dictGetOrEmpty(d, "userinfo")
|
||||
var user *url.Userinfo
|
||||
if userinfo != "" {
|
||||
tempURL, err := url.Parse(fmt.Sprintf("proto://%s@host", userinfo))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("unable to parse userinfo in dict: %s", err))
|
||||
}
|
||||
user = tempURL.User
|
||||
}
|
||||
|
||||
resURL.User = user
|
||||
return resURL.String()
|
||||
}
|
|
@ -13,6 +13,7 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"google.golang.org/protobuf/reflect/protodesc"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"google.golang.org/protobuf/reflect/protoregistry"
|
||||
"google.golang.org/protobuf/runtime/protoimpl"
|
||||
|
@ -62,14 +63,7 @@ func FileDescriptor(s filePath) fileDescGZIP {
|
|||
// Find the descriptor in the v2 registry.
|
||||
var b []byte
|
||||
if fd, _ := protoregistry.GlobalFiles.FindFileByPath(s); fd != nil {
|
||||
if fd, ok := fd.(interface{ ProtoLegacyRawDesc() []byte }); ok {
|
||||
b = fd.ProtoLegacyRawDesc()
|
||||
} else {
|
||||
// TODO: Use protodesc.ToFileDescriptorProto to construct
|
||||
// a descriptorpb.FileDescriptorProto and marshal it.
|
||||
// However, doing so causes the proto package to have a dependency
|
||||
// on descriptorpb, leading to cyclic dependency issues.
|
||||
}
|
||||
b, _ = Marshal(protodesc.ToFileDescriptorProto(fd))
|
||||
}
|
||||
|
||||
// Locally cache the raw descriptor form for the file.
|
||||
|
|
|
@ -765,7 +765,7 @@ func unescape(s string) (ch string, tail string, err error) {
|
|||
if i > utf8.MaxRune {
|
||||
return "", "", fmt.Errorf(`\%c%s is not a valid Unicode code point`, r, ss)
|
||||
}
|
||||
return string(i), s, nil
|
||||
return string(rune(i)), s, nil
|
||||
}
|
||||
return "", "", fmt.Errorf(`unknown escape \%c`, r)
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ const urlPrefix = "type.googleapis.com/"
|
|||
|
||||
// AnyMessageName returns the message name contained in an anypb.Any message.
|
||||
// Most type assertions should use the Is function instead.
|
||||
//
|
||||
// Deprecated: Call the any.MessageName method instead.
|
||||
func AnyMessageName(any *anypb.Any) (string, error) {
|
||||
name, err := anyMessageName(any)
|
||||
return string(name), err
|
||||
|
@ -38,6 +40,8 @@ func anyMessageName(any *anypb.Any) (protoreflect.FullName, error) {
|
|||
}
|
||||
|
||||
// MarshalAny marshals the given message m into an anypb.Any message.
|
||||
//
|
||||
// Deprecated: Call the anypb.New function instead.
|
||||
func MarshalAny(m proto.Message) (*anypb.Any, error) {
|
||||
switch dm := m.(type) {
|
||||
case DynamicAny:
|
||||
|
@ -58,6 +62,9 @@ func MarshalAny(m proto.Message) (*anypb.Any, error) {
|
|||
// Empty returns a new message of the type specified in an anypb.Any message.
|
||||
// It returns protoregistry.NotFound if the corresponding message type could not
|
||||
// be resolved in the global registry.
|
||||
//
|
||||
// Deprecated: Use protoregistry.GlobalTypes.FindMessageByName instead
|
||||
// to resolve the message name and create a new instance of it.
|
||||
func Empty(any *anypb.Any) (proto.Message, error) {
|
||||
name, err := anyMessageName(any)
|
||||
if err != nil {
|
||||
|
@ -76,6 +83,8 @@ func Empty(any *anypb.Any) (proto.Message, error) {
|
|||
//
|
||||
// The target message m may be a *DynamicAny message. If the underlying message
|
||||
// type could not be resolved, then this returns protoregistry.NotFound.
|
||||
//
|
||||
// Deprecated: Call the any.UnmarshalTo method instead.
|
||||
func UnmarshalAny(any *anypb.Any, m proto.Message) error {
|
||||
if dm, ok := m.(*DynamicAny); ok {
|
||||
if dm.Message == nil {
|
||||
|
@ -100,6 +109,8 @@ func UnmarshalAny(any *anypb.Any, m proto.Message) error {
|
|||
}
|
||||
|
||||
// Is reports whether the Any message contains a message of the specified type.
|
||||
//
|
||||
// Deprecated: Call the any.MessageIs method instead.
|
||||
func Is(any *anypb.Any, m proto.Message) bool {
|
||||
if any == nil || m == nil {
|
||||
return false
|
||||
|
@ -119,6 +130,9 @@ func Is(any *anypb.Any, m proto.Message) bool {
|
|||
// var x ptypes.DynamicAny
|
||||
// if err := ptypes.UnmarshalAny(a, &x); err != nil { ... }
|
||||
// fmt.Printf("unmarshaled message: %v", x.Message)
|
||||
//
|
||||
// Deprecated: Use the any.UnmarshalNew method instead to unmarshal
|
||||
// the any message contents into a new instance of the underlying message.
|
||||
type DynamicAny struct{ proto.Message }
|
||||
|
||||
func (m DynamicAny) String() string {
|
||||
|
|
|
@ -3,4 +3,8 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package ptypes provides functionality for interacting with well-known types.
|
||||
//
|
||||
// Deprecated: Well-known types have specialized functionality directly
|
||||
// injected into the generated packages for each message type.
|
||||
// See the deprecation notice for each function for the suggested alternative.
|
||||
package ptypes
|
||||
|
|
|
@ -21,6 +21,8 @@ const (
|
|||
|
||||
// Duration converts a durationpb.Duration to a time.Duration.
|
||||
// Duration returns an error if dur is invalid or overflows a time.Duration.
|
||||
//
|
||||
// Deprecated: Call the dur.AsDuration and dur.CheckValid methods instead.
|
||||
func Duration(dur *durationpb.Duration) (time.Duration, error) {
|
||||
if err := validateDuration(dur); err != nil {
|
||||
return 0, err
|
||||
|
@ -39,6 +41,8 @@ func Duration(dur *durationpb.Duration) (time.Duration, error) {
|
|||
}
|
||||
|
||||
// DurationProto converts a time.Duration to a durationpb.Duration.
|
||||
//
|
||||
// Deprecated: Call the durationpb.New function instead.
|
||||
func DurationProto(d time.Duration) *durationpb.Duration {
|
||||
nanos := d.Nanoseconds()
|
||||
secs := nanos / 1e9
|
||||
|
|
|
@ -33,6 +33,8 @@ const (
|
|||
//
|
||||
// A nil Timestamp returns an error. The first return value in that case is
|
||||
// undefined.
|
||||
//
|
||||
// Deprecated: Call the ts.AsTime and ts.CheckValid methods instead.
|
||||
func Timestamp(ts *timestamppb.Timestamp) (time.Time, error) {
|
||||
// Don't return the zero value on error, because corresponds to a valid
|
||||
// timestamp. Instead return whatever time.Unix gives us.
|
||||
|
@ -46,6 +48,8 @@ func Timestamp(ts *timestamppb.Timestamp) (time.Time, error) {
|
|||
}
|
||||
|
||||
// TimestampNow returns a google.protobuf.Timestamp for the current time.
|
||||
//
|
||||
// Deprecated: Call the timestamppb.Now function instead.
|
||||
func TimestampNow() *timestamppb.Timestamp {
|
||||
ts, err := TimestampProto(time.Now())
|
||||
if err != nil {
|
||||
|
@ -56,6 +60,8 @@ func TimestampNow() *timestamppb.Timestamp {
|
|||
|
||||
// TimestampProto converts the time.Time to a google.protobuf.Timestamp proto.
|
||||
// It returns an error if the resulting Timestamp is invalid.
|
||||
//
|
||||
// Deprecated: Call the timestamppb.New function instead.
|
||||
func TimestampProto(t time.Time) (*timestamppb.Timestamp, error) {
|
||||
ts := ×tamppb.Timestamp{
|
||||
Seconds: t.Unix(),
|
||||
|
@ -69,6 +75,9 @@ func TimestampProto(t time.Time) (*timestamppb.Timestamp, error) {
|
|||
|
||||
// TimestampString returns the RFC 3339 string for valid Timestamps.
|
||||
// For invalid Timestamps, it returns an error message in parentheses.
|
||||
//
|
||||
// Deprecated: Call the ts.AsTime method instead,
|
||||
// followed by a call to the Format method on the time.Time value.
|
||||
func TimestampString(ts *timestamppb.Timestamp) string {
|
||||
t, err := Timestamp(ts)
|
||||
if err != nil {
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
||||
indent_size = 2
|
|
@ -1,31 +0,0 @@
|
|||
dist: xenial
|
||||
group: travis_latest
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.14.x"
|
||||
- "1.15.x"
|
||||
|
||||
# first part of the GOARCH workaround
|
||||
# setting the GOARCH directly doesn't work, since the value will be overwritten later
|
||||
# so set it to a temporary environment variable first
|
||||
env:
|
||||
global:
|
||||
- TIMESCALE_FACTOR=20
|
||||
matrix:
|
||||
- TRAVIS_GOARCH=amd64 TESTMODE=integration
|
||||
- TRAVIS_GOARCH=386 TESTMODE=integration
|
||||
|
||||
# second part of the GOARCH workaround
|
||||
# now actually set the GOARCH env variable to the value of the temporary variable set earlier
|
||||
before_install:
|
||||
- travis_retry go get golang.org/x/tools/cmd/cover
|
||||
- travis_retry go get github.com/onsi/ginkgo/ginkgo
|
||||
- travis_retry go get github.com/onsi/gomega
|
||||
- export GOARCH=$TRAVIS_GOARCH
|
||||
- go env # for debugging
|
||||
- travis_retry go get -t ./...
|
||||
|
||||
script:
|
||||
- .travis/script.sh
|
|
@ -1,6 +1,21 @@
|
|||
# Changelog
|
||||
|
||||
## v0.20.0 (unreleased)
|
||||
## v0.22.0 (2021-07-25)
|
||||
|
||||
- Use `ReadBatch` to read multiple UDP packets from the socket with a single syscall
|
||||
- Add a config option (`Config.DisableVersionNegotiationPackets`) to disable sending of Version Negotiation packets
|
||||
- Drop support for QUIC draft versions 32 and 34
|
||||
- Remove the `RetireBugBackwardsCompatibilityMode`, which was intended to mitigate a bug when retiring connection IDs in quic-go in v0.17.2 and ealier
|
||||
|
||||
## v0.21.2 (2021-07-15)
|
||||
|
||||
- Update qtls (for Go 1.15, 1.16 and 1.17rc1) to include the fix for the crypto/tls panic (see https://groups.google.com/g/golang-dev/c/5LJ2V7rd-Ag/m/YGLHVBZ6AAAJ for details)
|
||||
|
||||
## v0.21.0 (2021-06-01)
|
||||
|
||||
- quic-go now supports RFC 9000!
|
||||
|
||||
## v0.20.0 (2021-03-19)
|
||||
|
||||
- Remove the `quic.Config.HandshakeTimeout`. Introduce a `quic.Config.HandshakeIdleTimeout`.
|
||||
|
||||
|
|
|
@ -8,17 +8,12 @@
|
|||
[![Windows Build Status](https://img.shields.io/appveyor/ci/lucas-clemente/quic-go/master.svg?style=flat-square&label=windows+build)](https://ci.appveyor.com/project/lucas-clemente/quic-go/branch/master)
|
||||
[![Code Coverage](https://img.shields.io/codecov/c/github/lucas-clemente/quic-go/master.svg?style=flat-square)](https://codecov.io/gh/lucas-clemente/quic-go/)
|
||||
|
||||
quic-go is an implementation of the [QUIC](https://en.wikipedia.org/wiki/QUIC) protocol in Go. It implements the [IETF QUIC draft-29](https://tools.ietf.org/html/draft-ietf-quic-transport-29), [draft-32](https://tools.ietf.org/html/draft-ietf-quic-transport-32) and [draft-34](https://tools.ietf.org/html/draft-ietf-quic-transport-34).
|
||||
|
||||
## Version compatibility
|
||||
|
||||
Since quic-go is under active development, there's no guarantee that two builds of different commits are interoperable. The QUIC version used in the *master* branch is just a placeholder, and should not be considered stable.
|
||||
|
||||
When using quic-go as a library, please always use a [tagged release](https://github.com/lucas-clemente/quic-go/releases). Only these releases use the official draft version numbers.
|
||||
quic-go is an implementation of the [QUIC protocol, RFC 9000](https://datatracker.ietf.org/doc/html/rfc9000) protocol in Go.
|
||||
In addition to RFC 9000, it currently implements the [IETF QUIC draft-29](https://tools.ietf.org/html/draft-ietf-quic-transport-29). Support for draft-29 will eventually be dropped, as it is phased out of the ecosystem.
|
||||
|
||||
## Guides
|
||||
|
||||
*We currently support Go 1.15+, with [Go modules](https://github.com/golang/go/wiki/Modules) support enabled.*
|
||||
*We currently support Go 1.16.x and Go 1.17.x.*
|
||||
|
||||
Running tests:
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ type client struct {
|
|||
session quicSession
|
||||
|
||||
tracer logging.ConnectionTracer
|
||||
tracingID uint64
|
||||
logger utils.Logger
|
||||
}
|
||||
|
||||
|
@ -202,8 +203,16 @@ func dialContext(
|
|||
}
|
||||
c.packetHandlers = packetHandlers
|
||||
|
||||
c.tracingID = nextSessionTracingID()
|
||||
if c.config.Tracer != nil {
|
||||
c.tracer = c.config.Tracer.TracerForConnection(protocol.PerspectiveClient, c.destConnID)
|
||||
c.tracer = c.config.Tracer.TracerForConnection(
|
||||
context.WithValue(ctx, SessionTracingKey, c.tracingID),
|
||||
protocol.PerspectiveClient,
|
||||
c.destConnID,
|
||||
)
|
||||
}
|
||||
if c.tracer != nil {
|
||||
c.tracer.StartedConnection(c.conn.LocalAddr(), c.conn.RemoteAddr(), c.srcConnID, c.destConnID)
|
||||
}
|
||||
if err := c.dial(ctx); err != nil {
|
||||
return nil, err
|
||||
|
@ -270,9 +279,6 @@ func newClient(
|
|||
|
||||
func (c *client) dial(ctx context.Context) error {
|
||||
c.logger.Infof("Starting new connection to %s (%s -> %s), source connection ID %s, destination connection ID %s, version %s", c.tlsConf.ServerName, c.conn.LocalAddr(), c.conn.RemoteAddr(), c.srcConnID, c.destConnID, c.version)
|
||||
if c.tracer != nil {
|
||||
c.tracer.StartedConnection(c.conn.LocalAddr(), c.conn.RemoteAddr(), c.version, c.srcConnID, c.destConnID)
|
||||
}
|
||||
|
||||
c.session = newClientSession(
|
||||
c.conn,
|
||||
|
@ -285,6 +291,7 @@ func (c *client) dial(ctx context.Context) error {
|
|||
c.use0RTT,
|
||||
c.hasNegotiatedVersion,
|
||||
c.tracer,
|
||||
c.tracingID,
|
||||
c.logger,
|
||||
c.version,
|
||||
)
|
||||
|
@ -293,7 +300,8 @@ func (c *client) dial(ctx context.Context) error {
|
|||
errorChan := make(chan error, 1)
|
||||
go func() {
|
||||
err := c.session.run() // returns as soon as the session is closed
|
||||
if !errors.Is(err, errCloseForRecreating{}) && c.createdPacketConn {
|
||||
|
||||
if e := (&errCloseForRecreating{}); !errors.As(err, &e) && c.createdPacketConn {
|
||||
c.packetHandlers.Destroy()
|
||||
}
|
||||
errorChan <- err
|
||||
|
|
|
@ -117,6 +117,7 @@ func populateConfig(config *Config) *Config {
|
|||
TokenStore: config.TokenStore,
|
||||
EnableDatagrams: config.EnableDatagrams,
|
||||
DisablePathMTUDiscovery: config.DisablePathMTUDiscovery,
|
||||
DisableVersionNegotiationPackets: config.DisableVersionNegotiationPackets,
|
||||
Tracer: config.Tracer,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,3 +15,7 @@ const (
|
|||
msgTypeIPv4PKTINFO = unix.IP_PKTINFO
|
||||
msgTypeIPv6PKTINFO = 0x2e
|
||||
)
|
||||
|
||||
// ReadBatch only returns a single packet on OSX,
|
||||
// see https://godoc.org/golang.org/x/net/ipv4#PacketConn.ReadBatch.
|
||||
const batchSize = 1
|
||||
|
|
|
@ -15,3 +15,5 @@ const (
|
|||
msgTypeIPv4PKTINFO = 0x7
|
||||
msgTypeIPv6PKTINFO = 0x2e
|
||||
)
|
||||
|
||||
const batchSize = 8
|
||||
|
|
|
@ -15,3 +15,5 @@ const (
|
|||
msgTypeIPv4PKTINFO = unix.IP_PKTINFO
|
||||
msgTypeIPv6PKTINFO = unix.IPV6_PKTINFO
|
||||
)
|
||||
|
||||
const batchSize = 8 // needs to smaller than MaxUint8 (otherwise the type of oobConn.readPos has to be changed)
|
||||
|
|
|
@ -73,15 +73,21 @@ func (m *connIDGenerator) SetMaxActiveConnIDs(limit uint64) error {
|
|||
|
||||
func (m *connIDGenerator) Retire(seq uint64, sentWithDestConnID protocol.ConnectionID) error {
|
||||
if seq > m.highestSeq {
|
||||
return qerr.NewError(qerr.ProtocolViolation, fmt.Sprintf("tried to retire connection ID %d. Highest issued: %d", seq, m.highestSeq))
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.ProtocolViolation,
|
||||
ErrorMessage: fmt.Sprintf("retired connection ID %d (highest issued: %d)", seq, m.highestSeq),
|
||||
}
|
||||
}
|
||||
connID, ok := m.activeSrcConnIDs[seq]
|
||||
// We might already have deleted this connection ID, if this is a duplicate frame.
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
if connID.Equal(sentWithDestConnID) && !protocol.UseRetireBugBackwardsCompatibilityMode(RetireBugBackwardsCompatibilityMode, m.version) {
|
||||
return qerr.NewError(qerr.ProtocolViolation, fmt.Sprintf("tried to retire connection ID %d (%s), which was used as the Destination Connection ID on this packet", seq, connID))
|
||||
if connID.Equal(sentWithDestConnID) {
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.ProtocolViolation,
|
||||
ErrorMessage: fmt.Sprintf("retired connection ID %d (%s), which was used as the Destination Connection ID on this packet", seq, connID),
|
||||
}
|
||||
}
|
||||
m.retireConnectionID(connID)
|
||||
delete(m.activeSrcConnIDs, seq)
|
||||
|
@ -93,9 +99,6 @@ func (m *connIDGenerator) Retire(seq uint64, sentWithDestConnID protocol.Connect
|
|||
}
|
||||
|
||||
func (m *connIDGenerator) issueNewConnID() error {
|
||||
if protocol.UseRetireBugBackwardsCompatibilityMode(RetireBugBackwardsCompatibilityMode, m.version) {
|
||||
return nil
|
||||
}
|
||||
connID, err := protocol.GenerateConnectionID(m.connIDLen)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -53,7 +53,7 @@ func (h *connIDManager) Add(f *wire.NewConnectionIDFrame) error {
|
|||
return err
|
||||
}
|
||||
if h.queue.Len() >= protocol.MaxActiveConnectionIDs {
|
||||
return qerr.ConnectionIDLimitError
|
||||
return &qerr.TransportError{ErrorCode: qerr.ConnectionIDLimitError}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -12,13 +12,27 @@ import (
|
|||
"time"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||
)
|
||||
|
||||
const ecnMask uint8 = 0x3
|
||||
const (
|
||||
ecnMask = 0x3
|
||||
oobBufferSize = 128
|
||||
)
|
||||
|
||||
// Contrary to what the naming suggests, the ipv{4,6}.Message is not dependent on the IP version.
|
||||
// They're both just aliases for x/net/internal/socket.Message.
|
||||
// This means we can use this struct to read from a socket that receives both IPv4 and IPv6 messages.
|
||||
var _ ipv4.Message = ipv6.Message{}
|
||||
|
||||
type batchConn interface {
|
||||
ReadBatch(ms []ipv4.Message, flags int) (int, error)
|
||||
}
|
||||
|
||||
func inspectReadBuffer(c interface{}) (int, error) {
|
||||
conn, ok := c.(interface {
|
||||
|
@ -43,7 +57,12 @@ func inspectReadBuffer(c interface{}) (int, error) {
|
|||
|
||||
type oobConn struct {
|
||||
OOBCapablePacketConn
|
||||
oobBuffer []byte
|
||||
batchConn batchConn
|
||||
|
||||
readPos uint8
|
||||
// Packets received from the kernel, but not yet returned by ReadPacket().
|
||||
messages []ipv4.Message
|
||||
buffers [batchSize]*packetBuffer
|
||||
}
|
||||
|
||||
var _ connection = &oobConn{}
|
||||
|
@ -94,23 +113,52 @@ func newConn(c OOBCapablePacketConn) (*oobConn, error) {
|
|||
return nil, errors.New("activating packet info failed for both IPv4 and IPv6")
|
||||
}
|
||||
}
|
||||
return &oobConn{
|
||||
|
||||
// Allows callers to pass in a connection that already satisfies batchConn interface
|
||||
// to make use of the optimisation. Otherwise, ipv4.NewPacketConn would unwrap the file descriptor
|
||||
// via SyscallConn(), and read it that way, which might not be what the caller wants.
|
||||
var bc batchConn
|
||||
if ibc, ok := c.(batchConn); ok {
|
||||
bc = ibc
|
||||
} else {
|
||||
bc = ipv4.NewPacketConn(c)
|
||||
}
|
||||
|
||||
oobConn := &oobConn{
|
||||
OOBCapablePacketConn: c,
|
||||
oobBuffer: make([]byte, 128),
|
||||
}, nil
|
||||
batchConn: bc,
|
||||
messages: make([]ipv4.Message, batchSize),
|
||||
readPos: batchSize,
|
||||
}
|
||||
for i := 0; i < batchSize; i++ {
|
||||
oobConn.messages[i].OOB = make([]byte, oobBufferSize)
|
||||
}
|
||||
return oobConn, nil
|
||||
}
|
||||
|
||||
func (c *oobConn) ReadPacket() (*receivedPacket, error) {
|
||||
if len(c.messages) == int(c.readPos) { // all messages read. Read the next batch of messages.
|
||||
c.messages = c.messages[:batchSize]
|
||||
// replace buffers data buffers up to the packet that has been consumed during the last ReadBatch call
|
||||
for i := uint8(0); i < c.readPos; i++ {
|
||||
buffer := getPacketBuffer()
|
||||
// The packet size should not exceed protocol.MaxPacketBufferSize bytes
|
||||
// If it does, we only read a truncated packet, which will then end up undecryptable
|
||||
buffer.Data = buffer.Data[:protocol.MaxPacketBufferSize]
|
||||
c.oobBuffer = c.oobBuffer[:cap(c.oobBuffer)]
|
||||
n, oobn, _, addr, err := c.OOBCapablePacketConn.ReadMsgUDP(buffer.Data, c.oobBuffer)
|
||||
if err != nil {
|
||||
c.buffers[i] = buffer
|
||||
c.messages[i].Buffers = [][]byte{c.buffers[i].Data}
|
||||
}
|
||||
c.readPos = 0
|
||||
|
||||
n, err := c.batchConn.ReadBatch(c.messages, 0)
|
||||
if n == 0 || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctrlMsgs, err := unix.ParseSocketControlMessage(c.oobBuffer[:oobn])
|
||||
c.messages = c.messages[:n]
|
||||
}
|
||||
|
||||
msg := c.messages[c.readPos]
|
||||
buffer := c.buffers[c.readPos]
|
||||
c.readPos++
|
||||
ctrlMsgs, err := unix.ParseSocketControlMessage(msg.OOB[:msg.NN])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -129,13 +177,15 @@ func (c *oobConn) ReadPacket() (*receivedPacket, error) {
|
|||
// struct in_addr ipi_addr; /* Header Destination
|
||||
// address */
|
||||
// };
|
||||
ip := make([]byte, 4)
|
||||
if len(ctrlMsg.Data) == 12 {
|
||||
ifIndex = binary.LittleEndian.Uint32(ctrlMsg.Data)
|
||||
destIP = net.IP(ctrlMsg.Data[8:12])
|
||||
copy(ip, ctrlMsg.Data[8:12])
|
||||
} else if len(ctrlMsg.Data) == 4 {
|
||||
// FreeBSD
|
||||
destIP = net.IP(ctrlMsg.Data)
|
||||
copy(ip, ctrlMsg.Data)
|
||||
}
|
||||
destIP = net.IP(ip)
|
||||
}
|
||||
}
|
||||
if ctrlMsg.Header.Level == unix.IPPROTO_IPV6 {
|
||||
|
@ -148,7 +198,9 @@ func (c *oobConn) ReadPacket() (*receivedPacket, error) {
|
|||
// unsigned int ipi6_ifindex; /* send/recv interface index */
|
||||
// };
|
||||
if len(ctrlMsg.Data) == 20 {
|
||||
destIP = net.IP(ctrlMsg.Data[:16])
|
||||
ip := make([]byte, 16)
|
||||
copy(ip, ctrlMsg.Data[:16])
|
||||
destIP = net.IP(ip)
|
||||
ifIndex = binary.LittleEndian.Uint32(ctrlMsg.Data[16:])
|
||||
}
|
||||
}
|
||||
|
@ -162,9 +214,9 @@ func (c *oobConn) ReadPacket() (*receivedPacket, error) {
|
|||
}
|
||||
}
|
||||
return &receivedPacket{
|
||||
remoteAddr: addr,
|
||||
remoteAddr: msg.Addr,
|
||||
rcvTime: time.Now(),
|
||||
data: buffer.Data[:n],
|
||||
data: msg.Buffers[0][:msg.N],
|
||||
ecn: ecn,
|
||||
info: info,
|
||||
buffer: buffer,
|
||||
|
|
|
@ -11,7 +11,21 @@ import (
|
|||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func newConn(c net.PacketConn) (connection, error) {
|
||||
const IP_DONTFRAGMENT = 14
|
||||
|
||||
func newConn(c OOBCapablePacketConn) (connection, error) {
|
||||
rawConn, err := c.SyscallConn()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't get syscall.RawConn: %w", err)
|
||||
}
|
||||
if err := rawConn.Control(func(fd uintptr) {
|
||||
// This should succeed if the connection is a IPv4 or a dual-stack connection.
|
||||
// It will fail for IPv6 connections.
|
||||
// TODO: properly handle error.
|
||||
_ = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, IP_DONTFRAGMENT, 1)
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &basicConn{PacketConn: c}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -39,12 +39,18 @@ func newCryptoStream() cryptoStream {
|
|||
func (s *cryptoStreamImpl) HandleCryptoFrame(f *wire.CryptoFrame) error {
|
||||
highestOffset := f.Offset + protocol.ByteCount(len(f.Data))
|
||||
if maxOffset := highestOffset; maxOffset > protocol.MaxCryptoStreamOffset {
|
||||
return qerr.NewError(qerr.CryptoBufferExceeded, fmt.Sprintf("received invalid offset %d on crypto stream, maximum allowed %d", maxOffset, protocol.MaxCryptoStreamOffset))
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.CryptoBufferExceeded,
|
||||
ErrorMessage: fmt.Sprintf("received invalid offset %d on crypto stream, maximum allowed %d", maxOffset, protocol.MaxCryptoStreamOffset),
|
||||
}
|
||||
}
|
||||
if s.finished {
|
||||
if highestOffset > s.highestOffset {
|
||||
// reject crypto data received after this stream was already finished
|
||||
return qerr.NewError(qerr.ProtocolViolation, "received crypto data after change of encryption level")
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.ProtocolViolation,
|
||||
ErrorMessage: "received crypto data after change of encryption level",
|
||||
}
|
||||
}
|
||||
// ignore data with a smaller offset than the highest received
|
||||
// could e.g. be a retransmission
|
||||
|
@ -80,7 +86,10 @@ func (s *cryptoStreamImpl) GetCryptoData() []byte {
|
|||
|
||||
func (s *cryptoStreamImpl) Finish() error {
|
||||
if s.queue.HasMoreData() {
|
||||
return qerr.NewError(qerr.ProtocolViolation, "encryption level changed, but crypto stream has more data to read")
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.ProtocolViolation,
|
||||
ErrorMessage: "encryption level changed, but crypto stream has more data to read",
|
||||
}
|
||||
}
|
||||
s.finished = true
|
||||
return nil
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
package quic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/qerr"
|
||||
)
|
||||
|
||||
type (
|
||||
TransportError = qerr.TransportError
|
||||
ApplicationError = qerr.ApplicationError
|
||||
VersionNegotiationError = qerr.VersionNegotiationError
|
||||
StatelessResetError = qerr.StatelessResetError
|
||||
IdleTimeoutError = qerr.IdleTimeoutError
|
||||
HandshakeTimeoutError = qerr.HandshakeTimeoutError
|
||||
)
|
||||
|
||||
type (
|
||||
TransportErrorCode = qerr.TransportErrorCode
|
||||
ApplicationErrorCode = qerr.ApplicationErrorCode
|
||||
StreamErrorCode = qerr.StreamErrorCode
|
||||
)
|
||||
|
||||
const (
|
||||
NoError = qerr.NoError
|
||||
InternalError = qerr.InternalError
|
||||
ConnectionRefused = qerr.ConnectionRefused
|
||||
FlowControlError = qerr.FlowControlError
|
||||
StreamLimitError = qerr.StreamLimitError
|
||||
StreamStateError = qerr.StreamStateError
|
||||
FinalSizeError = qerr.FinalSizeError
|
||||
FrameEncodingError = qerr.FrameEncodingError
|
||||
TransportParameterError = qerr.TransportParameterError
|
||||
ConnectionIDLimitError = qerr.ConnectionIDLimitError
|
||||
ProtocolViolation = qerr.ProtocolViolation
|
||||
InvalidToken = qerr.InvalidToken
|
||||
ApplicationErrorErrorCode = qerr.ApplicationErrorErrorCode
|
||||
CryptoBufferExceeded = qerr.CryptoBufferExceeded
|
||||
KeyUpdateError = qerr.KeyUpdateError
|
||||
AEADLimitReached = qerr.AEADLimitReached
|
||||
NoViablePathError = qerr.NoViablePathError
|
||||
)
|
||||
|
||||
// A StreamError is used for Stream.CancelRead and Stream.CancelWrite.
|
||||
// It is also returned from Stream.Read and Stream.Write if the peer canceled reading or writing.
|
||||
type StreamError struct {
|
||||
StreamID StreamID
|
||||
ErrorCode StreamErrorCode
|
||||
}
|
||||
|
||||
func (e *StreamError) Is(target error) bool {
|
||||
_, ok := target.(*StreamError)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (e *StreamError) Error() string {
|
||||
return fmt.Sprintf("stream %d canceled with error code %d", e.StreamID, e.ErrorCode)
|
||||
}
|
|
@ -1,19 +1,19 @@
|
|||
module github.com/lucas-clemente/quic-go
|
||||
|
||||
go 1.14
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/cheekybits/genny v1.0.0
|
||||
github.com/francoispqt/gojay v1.2.13
|
||||
github.com/golang/mock v1.5.0
|
||||
github.com/golang/mock v1.6.0
|
||||
github.com/marten-seemann/qpack v0.2.1
|
||||
github.com/marten-seemann/qtls-go1-15 v0.1.4
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.3
|
||||
github.com/onsi/ginkgo v1.14.0
|
||||
github.com/onsi/gomega v1.10.1
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.4
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.0
|
||||
github.com/onsi/ginkgo v1.16.4
|
||||
github.com/onsi/gomega v1.13.0
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
|
||||
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
)
|
||||
|
|
|
@ -16,6 +16,7 @@ github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitf
|
|||
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
|
@ -28,14 +29,16 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4
|
|||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
|
||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
|
@ -43,14 +46,16 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x
|
|||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
|
@ -75,25 +80,30 @@ github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm
|
|||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/marten-seemann/qpack v0.2.1 h1:jvTsT/HpCn2UZJdP+UUB53FfUUgeOyG5K1ns0OJOGVs=
|
||||
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
|
||||
github.com/marten-seemann/qtls-go1-15 v0.1.4 h1:RehYMOyRW8hPVEja1KBVsFVNSm35Jj9Mvs5yNoZZ28A=
|
||||
github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.3 h1:XEZ1xGorVy9u+lJq+WXNE+hiqRYLNvJGYmwfwKQN2gU=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.3/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.4 h1:xbHbOGGhrenVtII6Co8akhLEdrawwB2iHl5yhJRpnco=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.4/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.0 h1:P9ggrs5xtwiqXv/FHNwntmuLMNq3KaSIG93AtAZ48xk=
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.0/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
|
||||
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=
|
||||
github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
|
||||
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
@ -128,11 +138,15 @@ github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYED
|
|||
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
|
||||
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
||||
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||
|
@ -148,6 +162,8 @@ golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTk
|
|||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -160,8 +176,11 @@ golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
|
@ -172,8 +191,9 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -186,12 +206,20 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014 h1:joucsQqXmyBVxViHCPFjG3hx8JzIFSaym3l3MM/Jsdg=
|
||||
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
@ -201,10 +229,14 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
|
|||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.1 h1:wGiQel/hW0NnEkJUk8lbzkX2gFJU6PFxf1v5OlCfuOs=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
||||
|
@ -226,8 +258,10 @@ google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ
|
|||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
@ -238,8 +272,9 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
|
|||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
|
|
@ -12,16 +12,6 @@ import (
|
|||
"github.com/lucas-clemente/quic-go/logging"
|
||||
)
|
||||
|
||||
// RetireBugBackwardsCompatibilityMode controls a backwards compatibility mode, necessary due to a bug in
|
||||
// quic-go v0.17.2 (and earlier), where under certain circumstances, an endpoint would retire the connection
|
||||
// ID it is currently using. See https://github.com/lucas-clemente/quic-go/issues/2658.
|
||||
// The bug has now been fixed, and new deployments have nothing to worry about.
|
||||
// Deployments that already have quic-go <= v0.17.2 deployed should active RetireBugBackwardsCompatibilityMode.
|
||||
// If activated, quic-go will take steps to avoid the bug from triggering when connected to endpoints that are still
|
||||
// running quic-go <= v0.17.2.
|
||||
// This flag will be removed in a future version of quic-go.
|
||||
var RetireBugBackwardsCompatibilityMode bool
|
||||
|
||||
// The StreamID is the ID of a QUIC stream.
|
||||
type StreamID = protocol.StreamID
|
||||
|
||||
|
@ -31,8 +21,8 @@ type VersionNumber = protocol.VersionNumber
|
|||
const (
|
||||
// VersionDraft29 is IETF QUIC draft-29
|
||||
VersionDraft29 = protocol.VersionDraft29
|
||||
// VersionDraft32 is IETF QUIC draft-32
|
||||
VersionDraft32 = protocol.VersionDraft32
|
||||
// Version1 is RFC 9000
|
||||
Version1 = protocol.Version1
|
||||
)
|
||||
|
||||
// A Token can be used to verify the ownership of the client address.
|
||||
|
@ -62,10 +52,6 @@ type TokenStore interface {
|
|||
Put(key string, token *ClientToken)
|
||||
}
|
||||
|
||||
// An ErrorCode is an application-defined error code.
|
||||
// Valid values range between 0 and MAX_UINT62.
|
||||
type ErrorCode = protocol.ApplicationErrorCode
|
||||
|
||||
// Err0RTTRejected is the returned from:
|
||||
// * Open{Uni}Stream{Sync}
|
||||
// * Accept{Uni}Stream
|
||||
|
@ -73,7 +59,16 @@ type ErrorCode = protocol.ApplicationErrorCode
|
|||
// when the server rejects a 0-RTT connection attempt.
|
||||
var Err0RTTRejected = errors.New("0-RTT rejected")
|
||||
|
||||
// SessionTracingKey can be used to associate a ConnectionTracer with a Session.
|
||||
// It is set on the Session.Context() context,
|
||||
// as well as on the context passed to logging.Tracer.NewConnectionTracer.
|
||||
var SessionTracingKey = sessionTracingCtxKey{}
|
||||
|
||||
type sessionTracingCtxKey struct{}
|
||||
|
||||
// Stream is the interface implemented by QUIC streams
|
||||
// In addition to the errors listed on the Session,
|
||||
// calls to stream functions can return a StreamError if the stream is canceled.
|
||||
type Stream interface {
|
||||
ReceiveStream
|
||||
SendStream
|
||||
|
@ -99,7 +94,7 @@ type ReceiveStream interface {
|
|||
// It will ask the peer to stop transmitting stream data.
|
||||
// Read will unblock immediately, and future Read calls will fail.
|
||||
// When called multiple times or after reading the io.EOF it is a no-op.
|
||||
CancelRead(ErrorCode)
|
||||
CancelRead(StreamErrorCode)
|
||||
// SetReadDeadline sets the deadline for future Read calls and
|
||||
// any currently-blocked Read call.
|
||||
// A zero value for t means Read will not time out.
|
||||
|
@ -128,7 +123,7 @@ type SendStream interface {
|
|||
// Data already written, but not yet delivered to the peer is not guaranteed to be delivered reliably.
|
||||
// Write will unblock immediately, and future calls to Write will fail.
|
||||
// When called multiple times or after closing the stream it is a no-op.
|
||||
CancelWrite(ErrorCode)
|
||||
CancelWrite(StreamErrorCode)
|
||||
// The context is canceled as soon as the write-side of the stream is closed.
|
||||
// This happens when Close() or CancelWrite() is called, or when the peer
|
||||
// cancels the read-side of their stream.
|
||||
|
@ -142,14 +137,14 @@ type SendStream interface {
|
|||
SetWriteDeadline(t time.Time) error
|
||||
}
|
||||
|
||||
// StreamError is returned by Read and Write when the peer cancels the stream.
|
||||
type StreamError interface {
|
||||
error
|
||||
Canceled() bool
|
||||
ErrorCode() ErrorCode
|
||||
}
|
||||
|
||||
// A Session is a QUIC connection between two peers.
|
||||
// Calls to the session (and to streams) can return the following types of errors:
|
||||
// * ApplicationError: for errors triggered by the application running on top of QUIC
|
||||
// * TransportError: for errors triggered by the QUIC transport (in many cases a misbehaving peer)
|
||||
// * IdleTimeoutError: when the peer goes away unexpectedly (this is a net.Error timeout error)
|
||||
// * HandshakeTimeoutError: when the cryptographic handshake takes too long (this is a net.Error timeout error)
|
||||
// * StatelessResetError: when we receive a stateless reset (this is a net.Error temporary error)
|
||||
// * VersionNegotiationError: returned by the client, when there's no version overlap between the peers
|
||||
type Session interface {
|
||||
// AcceptStream returns the next stream opened by the peer, blocking until one is available.
|
||||
// If the session was closed due to a timeout, the error satisfies
|
||||
|
@ -185,9 +180,9 @@ type Session interface {
|
|||
LocalAddr() net.Addr
|
||||
// RemoteAddr returns the address of the peer.
|
||||
RemoteAddr() net.Addr
|
||||
// Close the connection with an error.
|
||||
// CloseWithError closes the connection with an error.
|
||||
// The error string will be sent to the peer.
|
||||
CloseWithError(ErrorCode, string) error
|
||||
CloseWithError(ApplicationErrorCode, string) error
|
||||
// The context is cancelled when the session is closed.
|
||||
// Warning: This API should not be considered stable and might change soon.
|
||||
Context() context.Context
|
||||
|
@ -289,6 +284,10 @@ type Config struct {
|
|||
// DisablePathMTUDiscovery disables Path MTU Discovery (RFC 8899).
|
||||
// Packets will then be at most 1252 (IPv4) / 1232 (IPv6) bytes in size.
|
||||
DisablePathMTUDiscovery bool
|
||||
// DisableVersionNegotiationPackets disables the sending of Version Negotiation packets.
|
||||
// This can be useful if version information is exchanged out-of-band.
|
||||
// It has no effect for a client.
|
||||
DisableVersionNegotiationPackets bool
|
||||
// See https://datatracker.ietf.org/doc/draft-ietf-quic-datagram/.
|
||||
// Datagrams will only be available when both peers enable datagram support.
|
||||
EnableDatagrams bool
|
||||
|
|
|
@ -9,12 +9,13 @@ import (
|
|||
// NewAckHandler creates a new SentPacketHandler and a new ReceivedPacketHandler
|
||||
func NewAckHandler(
|
||||
initialPacketNumber protocol.PacketNumber,
|
||||
initialMaxDatagramSize protocol.ByteCount,
|
||||
rttStats *utils.RTTStats,
|
||||
pers protocol.Perspective,
|
||||
tracer logging.ConnectionTracer,
|
||||
logger utils.Logger,
|
||||
version protocol.VersionNumber,
|
||||
) (SentPacketHandler, ReceivedPacketHandler) {
|
||||
sph := newSentPacketHandler(initialPacketNumber, rttStats, pers, tracer, logger)
|
||||
sph := newSentPacketHandler(initialPacketNumber, initialMaxDatagramSize, rttStats, pers, tracer, logger)
|
||||
return sph, newReceivedPacketHandler(sph, rttStats, logger, version)
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ type Packet struct {
|
|||
type SentPacketHandler interface {
|
||||
// SentPacket may modify the packet
|
||||
SentPacket(packet *Packet)
|
||||
ReceivedAck(ackFrame *wire.AckFrame, encLevel protocol.EncryptionLevel, recvTime time.Time) error
|
||||
ReceivedAck(ackFrame *wire.AckFrame, encLevel protocol.EncryptionLevel, recvTime time.Time) (bool /* 1-RTT packet acked */, error)
|
||||
ReceivedBytes(protocol.ByteCount)
|
||||
DropPackets(protocol.EncryptionLevel)
|
||||
ResetForRetry() error
|
||||
|
|
173
vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/sent_packet_handler.go
generated
vendored
173
vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/sent_packet_handler.go
generated
vendored
|
@ -21,6 +21,8 @@ const (
|
|||
packetThreshold = 3
|
||||
// Before validating the client's address, the server won't send more than 3x bytes than it received.
|
||||
amplificationFactor = 3
|
||||
// We use Retry packets to derive an RTT estimate. Make sure we don't set the RTT to a super low value yet.
|
||||
minRTTAfterRetry = 5 * time.Millisecond
|
||||
)
|
||||
|
||||
type packetNumberSpace struct {
|
||||
|
@ -101,6 +103,7 @@ var (
|
|||
|
||||
func newSentPacketHandler(
|
||||
initialPN protocol.PacketNumber,
|
||||
initialMaxDatagramSize protocol.ByteCount,
|
||||
rttStats *utils.RTTStats,
|
||||
pers protocol.Perspective,
|
||||
tracer logging.ConnectionTracer,
|
||||
|
@ -109,6 +112,7 @@ func newSentPacketHandler(
|
|||
congestion := congestion.NewCubicSender(
|
||||
congestion.DefaultClock{},
|
||||
rttStats,
|
||||
initialMaxDatagramSize,
|
||||
true, // use Reno
|
||||
tracer,
|
||||
)
|
||||
|
@ -194,12 +198,17 @@ func (h *sentPacketHandler) dropPackets(encLevel protocol.EncryptionLevel) {
|
|||
}
|
||||
|
||||
func (h *sentPacketHandler) ReceivedBytes(n protocol.ByteCount) {
|
||||
wasAmplificationLimit := h.isAmplificationLimited()
|
||||
h.bytesReceived += n
|
||||
if wasAmplificationLimit && !h.isAmplificationLimited() {
|
||||
h.setLossDetectionTimer()
|
||||
}
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) ReceivedPacket(encLevel protocol.EncryptionLevel) {
|
||||
if h.perspective == protocol.PerspectiveServer && encLevel == protocol.EncryptionHandshake {
|
||||
func (h *sentPacketHandler) ReceivedPacket(l protocol.EncryptionLevel) {
|
||||
if h.perspective == protocol.PerspectiveServer && l == protocol.EncryptionHandshake && !h.peerAddressValidated {
|
||||
h.peerAddressValidated = true
|
||||
h.setLossDetectionTimer()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -268,12 +277,15 @@ func (h *sentPacketHandler) sentPacketImpl(packet *Packet) bool /* is ack-elicit
|
|||
return isAckEliciting
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.EncryptionLevel, rcvTime time.Time) error {
|
||||
func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.EncryptionLevel, rcvTime time.Time) (bool /* contained 1-RTT packet */, error) {
|
||||
pnSpace := h.getPacketNumberSpace(encLevel)
|
||||
|
||||
largestAcked := ack.LargestAcked()
|
||||
if largestAcked > pnSpace.largestSent {
|
||||
return qerr.NewError(qerr.ProtocolViolation, "Received ACK for an unsent packet")
|
||||
return false, &qerr.TransportError{
|
||||
ErrorCode: qerr.ProtocolViolation,
|
||||
ErrorMessage: "received ACK for an unsent packet",
|
||||
}
|
||||
}
|
||||
|
||||
pnSpace.largestAcked = utils.MaxPacketNumber(pnSpace.largestAcked, largestAcked)
|
||||
|
@ -290,7 +302,7 @@ func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.En
|
|||
priorInFlight := h.bytesInFlight
|
||||
ackedPackets, err := h.detectAndRemoveAckedPackets(ack, encLevel)
|
||||
if err != nil || len(ackedPackets) == 0 {
|
||||
return err
|
||||
return false, err
|
||||
}
|
||||
// update the RTT, if the largest acked is newly acknowledged
|
||||
if len(ackedPackets) > 0 {
|
||||
|
@ -308,15 +320,16 @@ func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.En
|
|||
}
|
||||
}
|
||||
if err := h.detectLostPackets(rcvTime, encLevel); err != nil {
|
||||
return err
|
||||
return false, err
|
||||
}
|
||||
var acked1RTTPacket bool
|
||||
for _, p := range ackedPackets {
|
||||
if p.skippedPacket {
|
||||
return fmt.Errorf("received an ACK for skipped packet number: %d (%s)", p.PacketNumber, encLevel)
|
||||
}
|
||||
if p.includedInBytesInFlight && !p.declaredLost {
|
||||
h.congestion.OnPacketAcked(p.PacketNumber, p.Length, priorInFlight, rcvTime)
|
||||
}
|
||||
if p.EncryptionLevel == protocol.Encryption1RTT {
|
||||
acked1RTTPacket = true
|
||||
}
|
||||
h.removeFromBytesInFlight(p)
|
||||
}
|
||||
|
||||
|
@ -335,7 +348,7 @@ func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.En
|
|||
|
||||
pnSpace.history.DeleteOldPackets(rcvTime)
|
||||
h.setLossDetectionTimer()
|
||||
return nil
|
||||
return acked1RTTPacket, nil
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) GetLowestPacketNotConfirmedAcked() protocol.PacketNumber {
|
||||
|
@ -367,15 +380,20 @@ func (h *sentPacketHandler) detectAndRemoveAckedPackets(ack *wire.AckFrame, encL
|
|||
ackRange = ack.AckRanges[len(ack.AckRanges)-1-ackRangeIndex]
|
||||
}
|
||||
|
||||
if p.PacketNumber >= ackRange.Smallest { // packet i contained in ACK range
|
||||
if p.PacketNumber < ackRange.Smallest { // packet not contained in ACK range
|
||||
return true, nil
|
||||
}
|
||||
if p.PacketNumber > ackRange.Largest {
|
||||
return false, fmt.Errorf("BUG: ackhandler would have acked wrong packet %d, while evaluating range %d -> %d", p.PacketNumber, ackRange.Smallest, ackRange.Largest)
|
||||
}
|
||||
h.ackedPackets = append(h.ackedPackets, p)
|
||||
}
|
||||
} else {
|
||||
h.ackedPackets = append(h.ackedPackets, p)
|
||||
if p.skippedPacket {
|
||||
return false, &qerr.TransportError{
|
||||
ErrorCode: qerr.ProtocolViolation,
|
||||
ErrorMessage: fmt.Sprintf("received an ACK for skipped packet number: %d (%s)", p.PacketNumber, encLevel),
|
||||
}
|
||||
}
|
||||
h.ackedPackets = append(h.ackedPackets, p)
|
||||
return true, nil
|
||||
})
|
||||
if h.logger.Debug() && len(h.ackedPackets) > 0 {
|
||||
|
@ -399,6 +417,9 @@ func (h *sentPacketHandler) detectAndRemoveAckedPackets(ack *wire.AckFrame, encL
|
|||
if err := pnSpace.history.Remove(p.PacketNumber); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if h.tracer != nil {
|
||||
h.tracer.AcknowledgedPacket(encLevel, p.PacketNumber)
|
||||
}
|
||||
}
|
||||
|
||||
return h.ackedPackets, err
|
||||
|
@ -424,20 +445,20 @@ func (h *sentPacketHandler) getLossTimeAndSpace() (time.Time, protocol.Encryptio
|
|||
}
|
||||
|
||||
// same logic as getLossTimeAndSpace, but for lastAckElicitingPacketTime instead of lossTime
|
||||
func (h *sentPacketHandler) getPTOTimeAndSpace() (time.Time, protocol.EncryptionLevel) {
|
||||
if !h.hasOutstandingPackets() {
|
||||
func (h *sentPacketHandler) getPTOTimeAndSpace() (pto time.Time, encLevel protocol.EncryptionLevel, ok bool) {
|
||||
// We only send application data probe packets once the handshake is confirmed,
|
||||
// because before that, we don't have the keys to decrypt ACKs sent in 1-RTT packets.
|
||||
if !h.handshakeConfirmed && !h.hasOutstandingCryptoPackets() {
|
||||
if h.peerCompletedAddressValidation {
|
||||
return
|
||||
}
|
||||
t := time.Now().Add(h.rttStats.PTO(false) << h.ptoCount)
|
||||
if h.initialPackets != nil {
|
||||
return t, protocol.EncryptionInitial
|
||||
return t, protocol.EncryptionInitial, true
|
||||
}
|
||||
return t, protocol.EncryptionHandshake
|
||||
return t, protocol.EncryptionHandshake, true
|
||||
}
|
||||
|
||||
var (
|
||||
encLevel protocol.EncryptionLevel
|
||||
pto time.Time
|
||||
)
|
||||
|
||||
if h.initialPackets != nil {
|
||||
encLevel = protocol.EncryptionInitial
|
||||
if t := h.initialPackets.lastAckElicitingPacketTime; !t.IsZero() {
|
||||
|
@ -458,30 +479,27 @@ func (h *sentPacketHandler) getPTOTimeAndSpace() (time.Time, protocol.Encryption
|
|||
encLevel = protocol.Encryption1RTT
|
||||
}
|
||||
}
|
||||
return pto, encLevel
|
||||
return pto, encLevel, true
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) hasOutstandingCryptoPackets() bool {
|
||||
var hasInitial, hasHandshake bool
|
||||
if h.initialPackets != nil {
|
||||
hasInitial = h.initialPackets.history.HasOutstandingPackets()
|
||||
if h.initialPackets != nil && h.initialPackets.history.HasOutstandingPackets() {
|
||||
return true
|
||||
}
|
||||
if h.handshakePackets != nil {
|
||||
hasHandshake = h.handshakePackets.history.HasOutstandingPackets()
|
||||
if h.handshakePackets != nil && h.handshakePackets.history.HasOutstandingPackets() {
|
||||
return true
|
||||
}
|
||||
return hasInitial || hasHandshake
|
||||
return false
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) hasOutstandingPackets() bool {
|
||||
// We only send application data probe packets once the handshake completes,
|
||||
// because before that, we don't have the keys to decrypt ACKs sent in 1-RTT packets.
|
||||
return (h.handshakeConfirmed && h.appDataPackets.history.HasOutstandingPackets()) ||
|
||||
h.hasOutstandingCryptoPackets()
|
||||
return h.appDataPackets.history.HasOutstandingPackets() || h.hasOutstandingCryptoPackets()
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) setLossDetectionTimer() {
|
||||
oldAlarm := h.alarm // only needed in case tracing is enabled
|
||||
if lossTime, encLevel := h.getLossTimeAndSpace(); !lossTime.IsZero() {
|
||||
lossTime, encLevel := h.getLossTimeAndSpace()
|
||||
if !lossTime.IsZero() {
|
||||
// Early retransmit timer or time loss detection.
|
||||
h.alarm = lossTime
|
||||
if h.tracer != nil && h.alarm != oldAlarm {
|
||||
|
@ -490,18 +508,42 @@ func (h *sentPacketHandler) setLossDetectionTimer() {
|
|||
return
|
||||
}
|
||||
|
||||
// Cancel the alarm if amplification limited.
|
||||
if h.isAmplificationLimited() {
|
||||
h.alarm = time.Time{}
|
||||
if !oldAlarm.IsZero() {
|
||||
h.logger.Debugf("Canceling loss detection timer. Amplification limited.")
|
||||
if h.tracer != nil {
|
||||
h.tracer.LossTimerCanceled()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Cancel the alarm if no packets are outstanding
|
||||
if !h.hasOutstandingPackets() && h.peerCompletedAddressValidation {
|
||||
h.alarm = time.Time{}
|
||||
if !oldAlarm.IsZero() {
|
||||
h.logger.Debugf("Canceling loss detection timer. No packets in flight.")
|
||||
if h.tracer != nil && !oldAlarm.IsZero() {
|
||||
if h.tracer != nil {
|
||||
h.tracer.LossTimerCanceled()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// PTO alarm
|
||||
ptoTime, encLevel := h.getPTOTimeAndSpace()
|
||||
ptoTime, encLevel, ok := h.getPTOTimeAndSpace()
|
||||
if !ok {
|
||||
if !oldAlarm.IsZero() {
|
||||
h.alarm = time.Time{}
|
||||
h.logger.Debugf("Canceling loss detection timer. No PTO needed..")
|
||||
if h.tracer != nil {
|
||||
h.tracer.LossTimerCanceled()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
h.alarm = ptoTime
|
||||
if h.tracer != nil && h.alarm != oldAlarm {
|
||||
h.tracer.SetLossTimer(logging.TimerTypePTO, encLevel, h.alarm)
|
||||
|
@ -569,20 +611,7 @@ func (h *sentPacketHandler) detectLostPackets(now time.Time, encLevel protocol.E
|
|||
}
|
||||
|
||||
func (h *sentPacketHandler) OnLossDetectionTimeout() error {
|
||||
// When all outstanding are acknowledged, the alarm is canceled in
|
||||
// setLossDetectionTimer. This doesn't reset the timer in the session though.
|
||||
// When OnAlarm is called, we therefore need to make sure that there are
|
||||
// actually packets outstanding.
|
||||
if h.hasOutstandingPackets() || !h.peerCompletedAddressValidation {
|
||||
if err := h.onVerifiedLossDetectionTimeout(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
h.setLossDetectionTimer()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) onVerifiedLossDetectionTimeout() error {
|
||||
defer h.setLossDetectionTimer()
|
||||
earliestLossTime, encLevel := h.getLossTimeAndSpace()
|
||||
if !earliestLossTime.IsZero() {
|
||||
if h.logger.Debug() {
|
||||
|
@ -596,9 +625,31 @@ func (h *sentPacketHandler) onVerifiedLossDetectionTimeout() error {
|
|||
}
|
||||
|
||||
// PTO
|
||||
// When all outstanding are acknowledged, the alarm is canceled in
|
||||
// setLossDetectionTimer. This doesn't reset the timer in the session though.
|
||||
// When OnAlarm is called, we therefore need to make sure that there are
|
||||
// actually packets outstanding.
|
||||
if h.bytesInFlight == 0 && !h.peerCompletedAddressValidation {
|
||||
h.ptoCount++
|
||||
h.numProbesToSend++
|
||||
if h.initialPackets != nil {
|
||||
h.ptoMode = SendPTOInitial
|
||||
} else if h.handshakePackets != nil {
|
||||
h.ptoMode = SendPTOHandshake
|
||||
} else {
|
||||
return errors.New("sentPacketHandler BUG: PTO fired, but bytes_in_flight is 0 and Initial and Handshake already dropped")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_, encLevel, ok := h.getPTOTimeAndSpace()
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
if ps := h.getPacketNumberSpace(encLevel); !ps.history.HasOutstandingPackets() && !h.peerCompletedAddressValidation {
|
||||
return nil
|
||||
}
|
||||
h.ptoCount++
|
||||
if h.bytesInFlight > 0 {
|
||||
_, encLevel = h.getPTOTimeAndSpace()
|
||||
if h.logger.Debug() {
|
||||
h.logger.Debugf("Loss detection alarm for %s fired in PTO mode. PTO count: %d", encLevel, h.ptoCount)
|
||||
}
|
||||
|
@ -620,19 +671,6 @@ func (h *sentPacketHandler) onVerifiedLossDetectionTimeout() error {
|
|||
default:
|
||||
return fmt.Errorf("PTO timer in unexpected encryption level: %s", encLevel)
|
||||
}
|
||||
} else {
|
||||
if h.perspective == protocol.PerspectiveServer {
|
||||
return errors.New("sentPacketHandler BUG: PTO fired, but bytes_in_flight is 0")
|
||||
}
|
||||
h.numProbesToSend++
|
||||
if h.initialPackets != nil {
|
||||
h.ptoMode = SendPTOInitial
|
||||
} else if h.handshakePackets != nil {
|
||||
h.ptoMode = SendPTOHandshake
|
||||
} else {
|
||||
return errors.New("sentPacketHandler BUG: PTO fired, but bytes_in_flight is 0 and Initial and Handshake already dropped")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -768,8 +806,9 @@ func (h *sentPacketHandler) ResetForRetry() error {
|
|||
// Only use the Retry to estimate the RTT if we didn't send any retransmission for the Initial.
|
||||
// Otherwise, we don't know which Initial the Retry was sent in response to.
|
||||
if h.ptoCount == 0 {
|
||||
// Don't set the RTT to a value lower than 5ms here.
|
||||
now := time.Now()
|
||||
h.rttStats.UpdateRTT(now.Sub(firstPacketSendTime), 0, now)
|
||||
h.rttStats.UpdateRTT(utils.MaxDuration(minRTTAfterRetry, now.Sub(firstPacketSendTime)), 0, now)
|
||||
if h.logger.Debug() {
|
||||
h.logger.Debugf("\tupdated RTT: %s (σ: %s)", h.rttStats.SmoothedRTT(), h.rttStats.MeanDeviation())
|
||||
}
|
||||
|
|
5
vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/sent_packet_history.go
generated
vendored
5
vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/sent_packet_history.go
generated
vendored
|
@ -64,8 +64,9 @@ func (h *sentPacketHistory) Iterate(cb func(*Packet) (cont bool, err error)) err
|
|||
// FirstOutStanding returns the first outstanding packet.
|
||||
func (h *sentPacketHistory) FirstOutstanding() *Packet {
|
||||
for el := h.packetList.Front(); el != nil; el = el.Next() {
|
||||
if !el.Value.declaredLost && !el.Value.skippedPacket {
|
||||
return &el.Value
|
||||
p := &el.Value
|
||||
if !p.declaredLost && !p.skippedPacket && !p.IsPathMTUProbePacket {
|
||||
return p
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package congestion
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
|
@ -14,9 +15,8 @@ const (
|
|||
initialMaxDatagramSize = protocol.ByteCount(protocol.InitialPacketSizeIPv4)
|
||||
maxBurstPackets = 3
|
||||
renoBeta = 0.7 // Reno backoff factor.
|
||||
initialMaxCongestionWindow = protocol.MaxCongestionWindowPackets * initialMaxDatagramSize
|
||||
minCongestionWindowPackets = 2
|
||||
initialCongestionWindow = 32 * initialMaxDatagramSize
|
||||
initialCongestionWindow = 32
|
||||
)
|
||||
|
||||
type cubicSender struct {
|
||||
|
@ -65,11 +65,33 @@ var (
|
|||
)
|
||||
|
||||
// NewCubicSender makes a new cubic sender
|
||||
func NewCubicSender(clock Clock, rttStats *utils.RTTStats, reno bool, tracer logging.ConnectionTracer) *cubicSender {
|
||||
return newCubicSender(clock, rttStats, reno, initialCongestionWindow, initialMaxCongestionWindow, tracer)
|
||||
func NewCubicSender(
|
||||
clock Clock,
|
||||
rttStats *utils.RTTStats,
|
||||
initialMaxDatagramSize protocol.ByteCount,
|
||||
reno bool,
|
||||
tracer logging.ConnectionTracer,
|
||||
) *cubicSender {
|
||||
return newCubicSender(
|
||||
clock,
|
||||
rttStats,
|
||||
reno,
|
||||
initialMaxDatagramSize,
|
||||
initialCongestionWindow*initialMaxDatagramSize,
|
||||
protocol.MaxCongestionWindowPackets*initialMaxDatagramSize,
|
||||
tracer,
|
||||
)
|
||||
}
|
||||
|
||||
func newCubicSender(clock Clock, rttStats *utils.RTTStats, reno bool, initialCongestionWindow, initialMaxCongestionWindow protocol.ByteCount, tracer logging.ConnectionTracer) *cubicSender {
|
||||
func newCubicSender(
|
||||
clock Clock,
|
||||
rttStats *utils.RTTStats,
|
||||
reno bool,
|
||||
initialMaxDatagramSize,
|
||||
initialCongestionWindow,
|
||||
initialMaxCongestionWindow protocol.ByteCount,
|
||||
tracer logging.ConnectionTracer,
|
||||
) *cubicSender {
|
||||
c := &cubicSender{
|
||||
rttStats: rttStats,
|
||||
largestSentPacketNumber: protocol.InvalidPacketNumber,
|
||||
|
@ -283,7 +305,7 @@ func (c *cubicSender) maybeTraceStateChange(new logging.CongestionState) {
|
|||
|
||||
func (c *cubicSender) SetMaxDatagramSize(s protocol.ByteCount) {
|
||||
if s < c.maxDatagramSize {
|
||||
panic("congestion BUG: decreased max datagram size")
|
||||
panic(fmt.Sprintf("congestion BUG: decreased max datagram size from %d to %d", c.maxDatagramSize, s))
|
||||
}
|
||||
cwndIsMinCwnd := c.congestionWindow == c.minCongestionWindow()
|
||||
c.maxDatagramSize = s
|
||||
|
|
|
@ -50,7 +50,10 @@ func (c *connectionFlowController) IncrementHighestReceived(increment protocol.B
|
|||
|
||||
c.highestReceived += increment
|
||||
if c.checkFlowControlViolation() {
|
||||
return qerr.NewError(qerr.FlowControlError, fmt.Sprintf("Received %d bytes for the connection, allowed %d bytes", c.highestReceived, c.receiveWindow))
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.FlowControlError,
|
||||
ErrorMessage: fmt.Sprintf("received %d bytes for the connection, allowed %d bytes", c.highestReceived, c.receiveWindow),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
25
vendor/github.com/lucas-clemente/quic-go/internal/flowcontrol/stream_flow_controller.go
generated
vendored
25
vendor/github.com/lucas-clemente/quic-go/internal/flowcontrol/stream_flow_controller.go
generated
vendored
|
@ -54,11 +54,17 @@ func (c *streamFlowController) UpdateHighestReceived(offset protocol.ByteCount,
|
|||
if c.receivedFinalOffset {
|
||||
// If we receive another final offset, check that it's the same.
|
||||
if final && offset != c.highestReceived {
|
||||
return qerr.NewError(qerr.FinalSizeError, fmt.Sprintf("Received inconsistent final offset for stream %d (old: %d, new: %d bytes)", c.streamID, c.highestReceived, offset))
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.FinalSizeError,
|
||||
ErrorMessage: fmt.Sprintf("received inconsistent final offset for stream %d (old: %d, new: %d bytes)", c.streamID, c.highestReceived, offset),
|
||||
}
|
||||
}
|
||||
// Check that the offset is below the final offset.
|
||||
if offset > c.highestReceived {
|
||||
return qerr.NewError(qerr.FinalSizeError, fmt.Sprintf("Received offset %d for stream %d. Final offset was already received at %d", offset, c.streamID, c.highestReceived))
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.FinalSizeError,
|
||||
ErrorMessage: fmt.Sprintf("received offset %d for stream %d, but final offset was already received at %d", offset, c.streamID, c.highestReceived),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,7 +78,10 @@ func (c *streamFlowController) UpdateHighestReceived(offset protocol.ByteCount,
|
|||
// This can happen due to reordering.
|
||||
if offset <= c.highestReceived {
|
||||
if final {
|
||||
return qerr.NewError(qerr.FinalSizeError, fmt.Sprintf("Received final offset %d for stream %d, but already received offset %d before", offset, c.streamID, c.highestReceived))
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.FinalSizeError,
|
||||
ErrorMessage: fmt.Sprintf("received final offset %d for stream %d, but already received offset %d before", offset, c.streamID, c.highestReceived),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -80,7 +89,10 @@ func (c *streamFlowController) UpdateHighestReceived(offset protocol.ByteCount,
|
|||
increment := offset - c.highestReceived
|
||||
c.highestReceived = offset
|
||||
if c.checkFlowControlViolation() {
|
||||
return qerr.NewError(qerr.FlowControlError, fmt.Sprintf("Received %d bytes on stream %d, allowed %d bytes", offset, c.streamID, c.receiveWindow))
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.FlowControlError,
|
||||
ErrorMessage: fmt.Sprintf("received %d bytes on stream %d, allowed %d bytes", offset, c.streamID, c.receiveWindow),
|
||||
}
|
||||
}
|
||||
return c.connection.IncrementHighestReceived(increment)
|
||||
}
|
||||
|
@ -97,7 +109,10 @@ func (c *streamFlowController) AddBytesRead(n protocol.ByteCount) {
|
|||
}
|
||||
|
||||
func (c *streamFlowController) Abandon() {
|
||||
if unread := c.highestReceived - c.bytesRead; unread > 0 {
|
||||
c.mutex.Lock()
|
||||
unread := c.highestReceived - c.bytesRead
|
||||
c.mutex.Unlock()
|
||||
if unread > 0 {
|
||||
c.connection.AddBytesRead(unread)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -403,7 +403,10 @@ func (h *cryptoSetup) checkEncryptionLevel(msgType messageType, encLevel protoco
|
|||
func (h *cryptoSetup) handleTransportParameters(data []byte) {
|
||||
var tp wire.TransportParameters
|
||||
if err := tp.Unmarshal(data, h.perspective.Opposite()); err != nil {
|
||||
h.runner.OnError(qerr.NewError(qerr.TransportParameterError, err.Error()))
|
||||
h.runner.OnError(&qerr.TransportError{
|
||||
ErrorCode: qerr.TransportParameterError,
|
||||
ErrorMessage: err.Error(),
|
||||
})
|
||||
}
|
||||
h.peerParams = &tp
|
||||
h.runner.OnReceivedParams(h.peerParams)
|
||||
|
@ -555,7 +558,7 @@ func (h *cryptoSetup) SetReadKey(encLevel qtls.EncryptionLevel, suite *qtls.Ciph
|
|||
newHeaderProtector(suite, trafficSecret, true),
|
||||
)
|
||||
h.mutex.Unlock()
|
||||
h.logger.Debugf("Installed 0-RTT Read keys (using %s)", qtls.CipherSuiteName(suite.ID))
|
||||
h.logger.Debugf("Installed 0-RTT Read keys (using %s)", tls.CipherSuiteName(suite.ID))
|
||||
if h.tracer != nil {
|
||||
h.tracer.UpdatedKeyFromTLS(protocol.Encryption0RTT, h.perspective.Opposite())
|
||||
}
|
||||
|
@ -568,12 +571,12 @@ func (h *cryptoSetup) SetReadKey(encLevel qtls.EncryptionLevel, suite *qtls.Ciph
|
|||
h.dropInitialKeys,
|
||||
h.perspective,
|
||||
)
|
||||
h.logger.Debugf("Installed Handshake Read keys (using %s)", qtls.CipherSuiteName(suite.ID))
|
||||
h.logger.Debugf("Installed Handshake Read keys (using %s)", tls.CipherSuiteName(suite.ID))
|
||||
case qtls.EncryptionApplication:
|
||||
h.readEncLevel = protocol.Encryption1RTT
|
||||
h.aead.SetReadKey(suite, trafficSecret)
|
||||
h.has1RTTOpener = true
|
||||
h.logger.Debugf("Installed 1-RTT Read keys (using %s)", qtls.CipherSuiteName(suite.ID))
|
||||
h.logger.Debugf("Installed 1-RTT Read keys (using %s)", tls.CipherSuiteName(suite.ID))
|
||||
default:
|
||||
panic("unexpected read encryption level")
|
||||
}
|
||||
|
@ -595,7 +598,7 @@ func (h *cryptoSetup) SetWriteKey(encLevel qtls.EncryptionLevel, suite *qtls.Cip
|
|||
newHeaderProtector(suite, trafficSecret, true),
|
||||
)
|
||||
h.mutex.Unlock()
|
||||
h.logger.Debugf("Installed 0-RTT Write keys (using %s)", qtls.CipherSuiteName(suite.ID))
|
||||
h.logger.Debugf("Installed 0-RTT Write keys (using %s)", tls.CipherSuiteName(suite.ID))
|
||||
if h.tracer != nil {
|
||||
h.tracer.UpdatedKeyFromTLS(protocol.Encryption0RTT, h.perspective)
|
||||
}
|
||||
|
@ -608,12 +611,12 @@ func (h *cryptoSetup) SetWriteKey(encLevel qtls.EncryptionLevel, suite *qtls.Cip
|
|||
h.dropInitialKeys,
|
||||
h.perspective,
|
||||
)
|
||||
h.logger.Debugf("Installed Handshake Write keys (using %s)", qtls.CipherSuiteName(suite.ID))
|
||||
h.logger.Debugf("Installed Handshake Write keys (using %s)", tls.CipherSuiteName(suite.ID))
|
||||
case qtls.EncryptionApplication:
|
||||
h.writeEncLevel = protocol.Encryption1RTT
|
||||
h.aead.SetWriteKey(suite, trafficSecret)
|
||||
h.has1RTTSealer = true
|
||||
h.logger.Debugf("Installed 1-RTT Write keys (using %s)", qtls.CipherSuiteName(suite.ID))
|
||||
h.logger.Debugf("Installed 1-RTT Write keys (using %s)", tls.CipherSuiteName(suite.ID))
|
||||
if h.zeroRTTSealer != nil {
|
||||
h.zeroRTTSealer = nil
|
||||
h.logger.Debugf("Dropping 0-RTT keys.")
|
||||
|
|
|
@ -4,18 +4,20 @@ import (
|
|||
"crypto"
|
||||
"crypto/tls"
|
||||
|
||||
"golang.org/x/crypto/hkdf"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/qtls"
|
||||
)
|
||||
|
||||
var (
|
||||
quicSaltOld = []byte{0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99}
|
||||
quicSaltDraft34 = []byte{0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a}
|
||||
quicSalt = []byte{0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a}
|
||||
)
|
||||
|
||||
func getSalt(v protocol.VersionNumber) []byte {
|
||||
if v == protocol.VersionDraft34 {
|
||||
return quicSaltDraft34
|
||||
if v == protocol.Version1 {
|
||||
return quicSalt
|
||||
}
|
||||
return quicSaltOld
|
||||
}
|
||||
|
@ -49,7 +51,7 @@ func NewInitialAEAD(connID protocol.ConnectionID, pers protocol.Perspective, v p
|
|||
}
|
||||
|
||||
func computeSecrets(connID protocol.ConnectionID, v protocol.VersionNumber) (clientSecret, serverSecret []byte) {
|
||||
initialSecret := qtls.HkdfExtract(crypto.SHA256, connID, getSalt(v))
|
||||
initialSecret := hkdf.Extract(crypto.SHA256.New, connID, getSalt(v))
|
||||
clientSecret = hkdfExpandLabel(crypto.SHA256, initialSecret, []byte{}, "client in", crypto.SHA256.Size())
|
||||
serverSecret = hkdfExpandLabel(crypto.SHA256, initialSecret, []byte{}, "server in", crypto.SHA256.Size())
|
||||
return
|
||||
|
|
|
@ -48,7 +48,7 @@ func GetRetryIntegrityTag(retry []byte, origDestConnID protocol.ConnectionID, ve
|
|||
|
||||
var tag [16]byte
|
||||
var sealed []byte
|
||||
if version != protocol.VersionDraft34 {
|
||||
if version != protocol.Version1 {
|
||||
sealed = oldRetryAEAD.Seal(tag[:0], oldRetryNonce[:], nil, retryBuf.Bytes())
|
||||
} else {
|
||||
sealed = retryAEAD.Seal(tag[:0], retryNonce[:], nil, retryBuf.Bytes())
|
||||
|
|
2
vendor/github.com/lucas-clemente/quic-go/internal/handshake/tls_extension_handler.go
generated
vendored
2
vendor/github.com/lucas-clemente/quic-go/internal/handshake/tls_extension_handler.go
generated
vendored
|
@ -24,7 +24,7 @@ var _ tlsExtensionHandler = &extensionHandler{}
|
|||
// newExtensionHandler creates a new extension handler
|
||||
func newExtensionHandler(params []byte, pers protocol.Perspective, v protocol.VersionNumber) tlsExtensionHandler {
|
||||
et := uint16(quicTLSExtensionType)
|
||||
if v != protocol.VersionDraft34 {
|
||||
if v != protocol.Version1 {
|
||||
et = quicTLSExtensionTypeOldDrafts
|
||||
}
|
||||
return &extensionHandler{
|
||||
|
|
|
@ -163,7 +163,7 @@ func (a *updatableAEAD) Open(dst, src []byte, rcvTime time.Time, pn protocol.Pac
|
|||
if err == ErrDecryptionFailed {
|
||||
a.invalidPacketCount++
|
||||
if a.invalidPacketCount >= a.invalidPacketLimit {
|
||||
return nil, qerr.AEADLimitReached
|
||||
return nil, &qerr.TransportError{ErrorCode: qerr.AEADLimitReached}
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
|
@ -201,7 +201,10 @@ func (a *updatableAEAD) open(dst, src []byte, rcvTime time.Time, pn protocol.Pac
|
|||
}
|
||||
// Opening succeeded. Check if the peer was allowed to update.
|
||||
if a.keyPhase > 0 && a.firstSentWithCurrentKey == protocol.InvalidPacketNumber {
|
||||
return nil, qerr.NewError(qerr.KeyUpdateError, "keys updated too quickly")
|
||||
return nil, &qerr.TransportError{
|
||||
ErrorCode: qerr.KeyUpdateError,
|
||||
ErrorMessage: "keys updated too quickly",
|
||||
}
|
||||
}
|
||||
a.rollKeys()
|
||||
a.logger.Debugf("Peer updated keys to %d", a.keyPhase)
|
||||
|
@ -250,7 +253,10 @@ func (a *updatableAEAD) Seal(dst, src []byte, pn protocol.PacketNumber, ad []byt
|
|||
func (a *updatableAEAD) SetLargestAcked(pn protocol.PacketNumber) error {
|
||||
if a.firstSentWithCurrentKey != protocol.InvalidPacketNumber &&
|
||||
pn >= a.firstSentWithCurrentKey && a.numRcvdWithCurrentKey == 0 {
|
||||
return qerr.NewError(qerr.KeyUpdateError, fmt.Sprintf("received ACK for key phase %d, but peer didn't update keys", a.keyPhase))
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.KeyUpdateError,
|
||||
ErrorMessage: fmt.Sprintf("received ACK for key phase %d, but peer didn't update keys", a.keyPhase),
|
||||
}
|
||||
}
|
||||
a.largestAcked = pn
|
||||
return nil
|
||||
|
|
|
@ -52,9 +52,6 @@ const MaxByteCount = ByteCount(1<<62 - 1)
|
|||
// InvalidByteCount is an invalid byte count
|
||||
const InvalidByteCount ByteCount = -1
|
||||
|
||||
// An ApplicationErrorCode is an application-defined error code.
|
||||
type ApplicationErrorCode uint64
|
||||
|
||||
// A StatelessResetToken is a stateless reset token.
|
||||
type StatelessResetToken [16]byte
|
||||
|
||||
|
|
|
@ -18,17 +18,16 @@ const (
|
|||
|
||||
// The version numbers, making grepping easier
|
||||
const (
|
||||
VersionTLS VersionNumber = 0xff00001d // draft-29
|
||||
VersionWhatever VersionNumber = 1 // for when the version doesn't matter
|
||||
VersionTLS VersionNumber = 0x1
|
||||
VersionWhatever VersionNumber = math.MaxUint32 - 1 // for when the version doesn't matter
|
||||
VersionUnknown VersionNumber = math.MaxUint32
|
||||
VersionDraft29 VersionNumber = 0xff00001d
|
||||
VersionDraft32 VersionNumber = 0xff000020
|
||||
VersionDraft34 VersionNumber = 0xff000022 // If everything goes according to plan at the IETF, this will one day be QUIC v1.
|
||||
Version1 VersionNumber = 0x1
|
||||
)
|
||||
|
||||
// SupportedVersions lists the versions that the server supports
|
||||
// must be in sorted descending order
|
||||
var SupportedVersions = []VersionNumber{VersionDraft29, VersionDraft34, VersionDraft32}
|
||||
var SupportedVersions = []VersionNumber{Version1, VersionDraft29}
|
||||
|
||||
// IsValidVersion says if the version is known to quic-go
|
||||
func IsValidVersion(v VersionNumber) bool {
|
||||
|
@ -38,7 +37,7 @@ func IsValidVersion(v VersionNumber) bool {
|
|||
func (vn VersionNumber) String() string {
|
||||
// For releases, VersionTLS will be set to a draft version.
|
||||
// A switch statement can't contain duplicate cases.
|
||||
if vn == VersionTLS && VersionTLS != VersionDraft29 && VersionTLS != VersionDraft32 {
|
||||
if vn == VersionTLS && VersionTLS != VersionDraft29 && VersionTLS != Version1 {
|
||||
return "TLS dev version (WIP)"
|
||||
}
|
||||
//nolint:exhaustive
|
||||
|
@ -49,10 +48,8 @@ func (vn VersionNumber) String() string {
|
|||
return "unknown"
|
||||
case VersionDraft29:
|
||||
return "draft-29"
|
||||
case VersionDraft32:
|
||||
return "draft-32"
|
||||
case VersionDraft34:
|
||||
return "draft-34"
|
||||
case Version1:
|
||||
return "v1"
|
||||
default:
|
||||
if vn.isGQUIC() {
|
||||
return fmt.Sprintf("gQUIC %d", vn.toGQUICVersion())
|
||||
|
@ -69,12 +66,6 @@ func (vn VersionNumber) toGQUICVersion() int {
|
|||
return int(10*(vn-gquicVersion0)/0x100) + int(vn%0x10)
|
||||
}
|
||||
|
||||
// UseRetireBugBackwardsCompatibilityMode says if it is necessary to use the backwards compatilibity mode.
|
||||
// This is only the case if it 1. is enabled and 2. draft-29 is used.
|
||||
func UseRetireBugBackwardsCompatibilityMode(enabled bool, v VersionNumber) bool {
|
||||
return enabled && v == VersionDraft29
|
||||
}
|
||||
|
||||
// IsSupportedVersion returns true if the server supports this version
|
||||
func IsSupportedVersion(supported []VersionNumber, v VersionNumber) bool {
|
||||
for _, t := range supported {
|
||||
|
@ -118,14 +109,3 @@ func GetGreasedVersions(supported []VersionNumber) []VersionNumber {
|
|||
copy(greased[randPos+1:], supported[randPos:])
|
||||
return greased
|
||||
}
|
||||
|
||||
// StripGreasedVersions strips all greased versions from a slice of versions
|
||||
func StripGreasedVersions(versions []VersionNumber) []VersionNumber {
|
||||
realVersions := make([]VersionNumber, 0, len(versions))
|
||||
for _, v := range versions {
|
||||
if v&0x0f0f0f0f != 0x0a0a0a0a {
|
||||
realVersions = append(realVersions, v)
|
||||
}
|
||||
}
|
||||
return realVersions
|
||||
}
|
||||
|
|
|
@ -6,51 +6,44 @@ import (
|
|||
"github.com/lucas-clemente/quic-go/internal/qtls"
|
||||
)
|
||||
|
||||
// ErrorCode can be used as a normal error without reason.
|
||||
type ErrorCode uint64
|
||||
// TransportErrorCode is a QUIC transport error.
|
||||
type TransportErrorCode uint64
|
||||
|
||||
// The error codes defined by QUIC
|
||||
const (
|
||||
NoError ErrorCode = 0x0
|
||||
InternalError ErrorCode = 0x1
|
||||
ConnectionRefused ErrorCode = 0x2
|
||||
FlowControlError ErrorCode = 0x3
|
||||
StreamLimitError ErrorCode = 0x4
|
||||
StreamStateError ErrorCode = 0x5
|
||||
FinalSizeError ErrorCode = 0x6
|
||||
FrameEncodingError ErrorCode = 0x7
|
||||
TransportParameterError ErrorCode = 0x8
|
||||
ConnectionIDLimitError ErrorCode = 0x9
|
||||
ProtocolViolation ErrorCode = 0xa
|
||||
InvalidToken ErrorCode = 0xb
|
||||
ApplicationError ErrorCode = 0xc
|
||||
CryptoBufferExceeded ErrorCode = 0xd
|
||||
KeyUpdateError ErrorCode = 0xe
|
||||
AEADLimitReached ErrorCode = 0xf
|
||||
NoViablePathError ErrorCode = 0x10
|
||||
NoError TransportErrorCode = 0x0
|
||||
InternalError TransportErrorCode = 0x1
|
||||
ConnectionRefused TransportErrorCode = 0x2
|
||||
FlowControlError TransportErrorCode = 0x3
|
||||
StreamLimitError TransportErrorCode = 0x4
|
||||
StreamStateError TransportErrorCode = 0x5
|
||||
FinalSizeError TransportErrorCode = 0x6
|
||||
FrameEncodingError TransportErrorCode = 0x7
|
||||
TransportParameterError TransportErrorCode = 0x8
|
||||
ConnectionIDLimitError TransportErrorCode = 0x9
|
||||
ProtocolViolation TransportErrorCode = 0xa
|
||||
InvalidToken TransportErrorCode = 0xb
|
||||
ApplicationErrorErrorCode TransportErrorCode = 0xc
|
||||
CryptoBufferExceeded TransportErrorCode = 0xd
|
||||
KeyUpdateError TransportErrorCode = 0xe
|
||||
AEADLimitReached TransportErrorCode = 0xf
|
||||
NoViablePathError TransportErrorCode = 0x10
|
||||
)
|
||||
|
||||
func (e ErrorCode) isCryptoError() bool {
|
||||
func (e TransportErrorCode) IsCryptoError() bool {
|
||||
return e >= 0x100 && e < 0x200
|
||||
}
|
||||
|
||||
func (e ErrorCode) Error() string {
|
||||
if e.isCryptoError() {
|
||||
return fmt.Sprintf("%s: %s", e.String(), e.Message())
|
||||
}
|
||||
return e.String()
|
||||
}
|
||||
|
||||
// Message is a description of the error.
|
||||
// It only returns a non-empty string for crypto errors.
|
||||
func (e ErrorCode) Message() string {
|
||||
if !e.isCryptoError() {
|
||||
func (e TransportErrorCode) Message() string {
|
||||
if !e.IsCryptoError() {
|
||||
return ""
|
||||
}
|
||||
return qtls.Alert(e - 0x100).Error()
|
||||
}
|
||||
|
||||
func (e ErrorCode) String() string {
|
||||
func (e TransportErrorCode) String() string {
|
||||
switch e {
|
||||
case NoError:
|
||||
return "NO_ERROR"
|
||||
|
@ -76,7 +69,7 @@ func (e ErrorCode) String() string {
|
|||
return "PROTOCOL_VIOLATION"
|
||||
case InvalidToken:
|
||||
return "INVALID_TOKEN"
|
||||
case ApplicationError:
|
||||
case ApplicationErrorErrorCode:
|
||||
return "APPLICATION_ERROR"
|
||||
case CryptoBufferExceeded:
|
||||
return "CRYPTO_BUFFER_EXCEEDED"
|
||||
|
@ -87,7 +80,7 @@ func (e ErrorCode) String() string {
|
|||
case NoViablePathError:
|
||||
return "NO_VIABLE_PATH"
|
||||
default:
|
||||
if e.isCryptoError() {
|
||||
if e.IsCryptoError() {
|
||||
return fmt.Sprintf("CRYPTO_ERROR (%#x)", uint16(e))
|
||||
}
|
||||
return fmt.Sprintf("unknown error code: %#x", uint16(e))
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
package qerr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrHandshakeTimeout = &HandshakeTimeoutError{}
|
||||
ErrIdleTimeout = &IdleTimeoutError{}
|
||||
)
|
||||
|
||||
type TransportError struct {
|
||||
Remote bool
|
||||
FrameType uint64
|
||||
ErrorCode TransportErrorCode
|
||||
ErrorMessage string
|
||||
}
|
||||
|
||||
var _ error = &TransportError{}
|
||||
|
||||
// NewCryptoError create a new TransportError instance for a crypto error
|
||||
func NewCryptoError(tlsAlert uint8, errorMessage string) *TransportError {
|
||||
return &TransportError{
|
||||
ErrorCode: 0x100 + TransportErrorCode(tlsAlert),
|
||||
ErrorMessage: errorMessage,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *TransportError) Error() string {
|
||||
str := e.ErrorCode.String()
|
||||
if e.FrameType != 0 {
|
||||
str += fmt.Sprintf(" (frame type: %#x)", e.FrameType)
|
||||
}
|
||||
msg := e.ErrorMessage
|
||||
if len(msg) == 0 {
|
||||
msg = e.ErrorCode.Message()
|
||||
}
|
||||
if len(msg) == 0 {
|
||||
return str
|
||||
}
|
||||
return str + ": " + msg
|
||||
}
|
||||
|
||||
func (e *TransportError) Is(target error) bool {
|
||||
return target == net.ErrClosed
|
||||
}
|
||||
|
||||
// An ApplicationErrorCode is an application-defined error code.
|
||||
type ApplicationErrorCode uint64
|
||||
|
||||
func (e *ApplicationError) Is(target error) bool {
|
||||
return target == net.ErrClosed
|
||||
}
|
||||
|
||||
// A StreamErrorCode is an error code used to cancel streams.
|
||||
type StreamErrorCode uint64
|
||||
|
||||
type ApplicationError struct {
|
||||
Remote bool
|
||||
ErrorCode ApplicationErrorCode
|
||||
ErrorMessage string
|
||||
}
|
||||
|
||||
var _ error = &ApplicationError{}
|
||||
|
||||
func (e *ApplicationError) Error() string {
|
||||
if len(e.ErrorMessage) == 0 {
|
||||
return fmt.Sprintf("Application error %#x", e.ErrorCode)
|
||||
}
|
||||
return fmt.Sprintf("Application error %#x: %s", e.ErrorCode, e.ErrorMessage)
|
||||
}
|
||||
|
||||
type IdleTimeoutError struct{}
|
||||
|
||||
var _ error = &IdleTimeoutError{}
|
||||
|
||||
func (e *IdleTimeoutError) Timeout() bool { return true }
|
||||
func (e *IdleTimeoutError) Temporary() bool { return false }
|
||||
func (e *IdleTimeoutError) Error() string { return "timeout: no recent network activity" }
|
||||
func (e *IdleTimeoutError) Is(target error) bool { return target == net.ErrClosed }
|
||||
|
||||
type HandshakeTimeoutError struct{}
|
||||
|
||||
var _ error = &HandshakeTimeoutError{}
|
||||
|
||||
func (e *HandshakeTimeoutError) Timeout() bool { return true }
|
||||
func (e *HandshakeTimeoutError) Temporary() bool { return false }
|
||||
func (e *HandshakeTimeoutError) Error() string { return "timeout: handshake did not complete in time" }
|
||||
func (e *HandshakeTimeoutError) Is(target error) bool { return target == net.ErrClosed }
|
||||
|
||||
// A VersionNegotiationError occurs when the client and the server can't agree on a QUIC version.
|
||||
type VersionNegotiationError struct {
|
||||
Ours []protocol.VersionNumber
|
||||
Theirs []protocol.VersionNumber
|
||||
}
|
||||
|
||||
func (e *VersionNegotiationError) Error() string {
|
||||
return fmt.Sprintf("no compatible QUIC version found (we support %s, server offered %s)", e.Ours, e.Theirs)
|
||||
}
|
||||
|
||||
func (e *VersionNegotiationError) Is(target error) bool {
|
||||
return target == net.ErrClosed
|
||||
}
|
||||
|
||||
// A StatelessResetError occurs when we receive a stateless reset.
|
||||
type StatelessResetError struct {
|
||||
Token protocol.StatelessResetToken
|
||||
}
|
||||
|
||||
var _ net.Error = &StatelessResetError{}
|
||||
|
||||
func (e *StatelessResetError) Error() string {
|
||||
return fmt.Sprintf("received a stateless reset with token %x", e.Token)
|
||||
}
|
||||
|
||||
func (e *StatelessResetError) Is(target error) bool {
|
||||
return target == net.ErrClosed
|
||||
}
|
||||
|
||||
func (e *StatelessResetError) Timeout() bool { return false }
|
||||
func (e *StatelessResetError) Temporary() bool { return true }
|
|
@ -1,112 +0,0 @@
|
|||
package qerr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
// A QuicError consists of an error code plus a error reason
|
||||
type QuicError struct {
|
||||
ErrorCode ErrorCode
|
||||
FrameType uint64 // only valid if this not an application error
|
||||
ErrorMessage string
|
||||
isTimeout bool
|
||||
isApplicationError bool
|
||||
}
|
||||
|
||||
var _ net.Error = &QuicError{}
|
||||
|
||||
// NewError creates a new QuicError instance
|
||||
func NewError(errorCode ErrorCode, errorMessage string) *QuicError {
|
||||
return &QuicError{
|
||||
ErrorCode: errorCode,
|
||||
ErrorMessage: errorMessage,
|
||||
}
|
||||
}
|
||||
|
||||
// NewErrorWithFrameType creates a new QuicError instance for a specific frame type
|
||||
func NewErrorWithFrameType(errorCode ErrorCode, frameType uint64, errorMessage string) *QuicError {
|
||||
return &QuicError{
|
||||
ErrorCode: errorCode,
|
||||
FrameType: frameType,
|
||||
ErrorMessage: errorMessage,
|
||||
}
|
||||
}
|
||||
|
||||
// NewTimeoutError creates a new QuicError instance for a timeout error
|
||||
func NewTimeoutError(errorMessage string) *QuicError {
|
||||
return &QuicError{
|
||||
ErrorMessage: errorMessage,
|
||||
isTimeout: true,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCryptoError create a new QuicError instance for a crypto error
|
||||
func NewCryptoError(tlsAlert uint8, errorMessage string) *QuicError {
|
||||
return &QuicError{
|
||||
ErrorCode: 0x100 + ErrorCode(tlsAlert),
|
||||
ErrorMessage: errorMessage,
|
||||
}
|
||||
}
|
||||
|
||||
// NewApplicationError creates a new QuicError instance for an application error
|
||||
func NewApplicationError(errorCode ErrorCode, errorMessage string) *QuicError {
|
||||
return &QuicError{
|
||||
ErrorCode: errorCode,
|
||||
ErrorMessage: errorMessage,
|
||||
isApplicationError: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *QuicError) Error() string {
|
||||
if e.isApplicationError {
|
||||
if len(e.ErrorMessage) == 0 {
|
||||
return fmt.Sprintf("Application error %#x", uint64(e.ErrorCode))
|
||||
}
|
||||
return fmt.Sprintf("Application error %#x: %s", uint64(e.ErrorCode), e.ErrorMessage)
|
||||
}
|
||||
str := e.ErrorCode.String()
|
||||
if e.FrameType != 0 {
|
||||
str += fmt.Sprintf(" (frame type: %#x)", e.FrameType)
|
||||
}
|
||||
msg := e.ErrorMessage
|
||||
if len(msg) == 0 {
|
||||
msg = e.ErrorCode.Message()
|
||||
}
|
||||
if len(msg) == 0 {
|
||||
return str
|
||||
}
|
||||
return str + ": " + msg
|
||||
}
|
||||
|
||||
// IsCryptoError says if this error is a crypto error
|
||||
func (e *QuicError) IsCryptoError() bool {
|
||||
return e.ErrorCode.isCryptoError()
|
||||
}
|
||||
|
||||
// IsApplicationError says if this error is an application error
|
||||
func (e *QuicError) IsApplicationError() bool {
|
||||
return e.isApplicationError
|
||||
}
|
||||
|
||||
// Temporary says if the error is temporary.
|
||||
func (e *QuicError) Temporary() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Timeout says if this error is a timeout.
|
||||
func (e *QuicError) Timeout() bool {
|
||||
return e.isTimeout
|
||||
}
|
||||
|
||||
// ToQuicError converts an arbitrary error to a QuicError. It leaves QuicErrors
|
||||
// unchanged, and properly handles `ErrorCode`s.
|
||||
func ToQuicError(err error) *QuicError {
|
||||
switch e := err.(type) {
|
||||
case *QuicError:
|
||||
return e
|
||||
case ErrorCode:
|
||||
return NewError(e, "")
|
||||
}
|
||||
return NewError(InternalError, err.Error())
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
// +build go1.16
|
||||
// +build !go1.17
|
||||
|
||||
package qtls
|
||||
|
||||
|
@ -9,7 +10,7 @@ import (
|
|||
"net"
|
||||
"unsafe"
|
||||
|
||||
qtls "github.com/marten-seemann/qtls-go1-16"
|
||||
"github.com/marten-seemann/qtls-go1-16"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -52,21 +53,6 @@ const (
|
|||
EncryptionApplication = qtls.EncryptionApplication
|
||||
)
|
||||
|
||||
// CipherSuiteName gets the name of a cipher suite.
|
||||
func CipherSuiteName(id uint16) string {
|
||||
return qtls.CipherSuiteName(id)
|
||||
}
|
||||
|
||||
// HkdfExtract generates a pseudorandom key for use with Expand from an input secret and an optional independent salt.
|
||||
func HkdfExtract(hash crypto.Hash, newSecret, currentSecret []byte) []byte {
|
||||
return qtls.HkdfExtract(hash, newSecret, currentSecret)
|
||||
}
|
||||
|
||||
// HkdfExpandLabel HKDF expands a label
|
||||
func HkdfExpandLabel(hash crypto.Hash, secret, hashValue []byte, label string, L int) []byte {
|
||||
return qtls.HkdfExpandLabel(hash, secret, hashValue, label, L)
|
||||
}
|
||||
|
||||
// AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3
|
||||
func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD {
|
||||
return qtls.AEADAESGCMTLS13(key, fixedNonce)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
// +build go1.15
|
||||
// +build !go1.16
|
||||
// +build go1.17
|
||||
|
||||
package qtls
|
||||
|
||||
|
@ -10,7 +9,7 @@ import (
|
|||
"net"
|
||||
"unsafe"
|
||||
|
||||
qtls "github.com/marten-seemann/qtls-go1-15"
|
||||
"github.com/marten-seemann/qtls-go1-17"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -53,21 +52,6 @@ const (
|
|||
EncryptionApplication = qtls.EncryptionApplication
|
||||
)
|
||||
|
||||
// CipherSuiteName gets the name of a cipher suite.
|
||||
func CipherSuiteName(id uint16) string {
|
||||
return qtls.CipherSuiteName(id)
|
||||
}
|
||||
|
||||
// HkdfExtract generates a pseudorandom key for use with Expand from an input secret and an optional independent salt.
|
||||
func HkdfExtract(hash crypto.Hash, newSecret, currentSecret []byte) []byte {
|
||||
return qtls.HkdfExtract(hash, newSecret, currentSecret)
|
||||
}
|
||||
|
||||
// HkdfExpandLabel HKDF expands a label
|
||||
func HkdfExpandLabel(hash crypto.Hash, secret, hashValue []byte, label string, L int) []byte {
|
||||
return qtls.HkdfExpandLabel(hash, secret, hashValue, label, L)
|
||||
}
|
||||
|
||||
// AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3
|
||||
func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD {
|
||||
return qtls.AEADAESGCMTLS13(key, fixedNonce)
|
||||
|
@ -99,7 +83,7 @@ type cipherSuiteTLS13 struct {
|
|||
Hash crypto.Hash
|
||||
}
|
||||
|
||||
//go:linkname cipherSuiteTLS13ByID github.com/marten-seemann/qtls-go1-15.cipherSuiteTLS13ByID
|
||||
//go:linkname cipherSuiteTLS13ByID github.com/marten-seemann/qtls-go1-17.cipherSuiteTLS13ByID
|
||||
func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13
|
||||
|
||||
// CipherSuiteTLS13ByID gets a TLS 1.3 cipher suite.
|
|
@ -0,0 +1,5 @@
|
|||
// +build go1.18
|
||||
|
||||
package qtls
|
||||
|
||||
var _ int = "quic-go doesn't build on Go 1.18 yet."
|
11
vendor/github.com/lucas-clemente/quic-go/internal/wire/connection_close_frame.go
generated
vendored
11
vendor/github.com/lucas-clemente/quic-go/internal/wire/connection_close_frame.go
generated
vendored
|
@ -5,14 +5,13 @@ import (
|
|||
"io"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/qerr"
|
||||
"github.com/lucas-clemente/quic-go/quicvarint"
|
||||
)
|
||||
|
||||
// A ConnectionCloseFrame is a CONNECTION_CLOSE frame
|
||||
type ConnectionCloseFrame struct {
|
||||
IsApplicationError bool
|
||||
ErrorCode qerr.ErrorCode
|
||||
ErrorCode uint64
|
||||
FrameType uint64
|
||||
ReasonPhrase string
|
||||
}
|
||||
|
@ -28,7 +27,7 @@ func parseConnectionCloseFrame(r *bytes.Reader, _ protocol.VersionNumber) (*Conn
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.ErrorCode = qerr.ErrorCode(ec)
|
||||
f.ErrorCode = ec
|
||||
// read the Frame Type, if this is not an application error
|
||||
if !f.IsApplicationError {
|
||||
ft, err := quicvarint.Read(r)
|
||||
|
@ -59,8 +58,8 @@ func parseConnectionCloseFrame(r *bytes.Reader, _ protocol.VersionNumber) (*Conn
|
|||
}
|
||||
|
||||
// Length of a written frame
|
||||
func (f *ConnectionCloseFrame) Length(version protocol.VersionNumber) protocol.ByteCount {
|
||||
length := 1 + quicvarint.Len(uint64(f.ErrorCode)) + quicvarint.Len(uint64(len(f.ReasonPhrase))) + protocol.ByteCount(len(f.ReasonPhrase))
|
||||
func (f *ConnectionCloseFrame) Length(protocol.VersionNumber) protocol.ByteCount {
|
||||
length := 1 + quicvarint.Len(f.ErrorCode) + quicvarint.Len(uint64(len(f.ReasonPhrase))) + protocol.ByteCount(len(f.ReasonPhrase))
|
||||
if !f.IsApplicationError {
|
||||
length += quicvarint.Len(f.FrameType) // for the frame type
|
||||
}
|
||||
|
@ -74,7 +73,7 @@ func (f *ConnectionCloseFrame) Write(b *bytes.Buffer, version protocol.VersionNu
|
|||
b.WriteByte(0x1c)
|
||||
}
|
||||
|
||||
quicvarint.Write(b, uint64(f.ErrorCode))
|
||||
quicvarint.Write(b, f.ErrorCode)
|
||||
if !f.IsApplicationError {
|
||||
quicvarint.Write(b, f.FrameType)
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ func NewFrameParser(supportsDatagrams bool, v protocol.VersionNumber) FrameParse
|
|||
}
|
||||
}
|
||||
|
||||
// ParseNextFrame parses the next frame
|
||||
// ParseNext parses the next frame.
|
||||
// It skips PADDING frames.
|
||||
func (p *frameParser) ParseNext(r *bytes.Reader, encLevel protocol.EncryptionLevel) (Frame, error) {
|
||||
for r.Len() != 0 {
|
||||
|
@ -38,7 +38,11 @@ func (p *frameParser) ParseNext(r *bytes.Reader, encLevel protocol.EncryptionLev
|
|||
|
||||
f, err := p.parseFrame(r, typeByte, encLevel)
|
||||
if err != nil {
|
||||
return nil, qerr.NewErrorWithFrameType(qerr.FrameEncodingError, uint64(typeByte), err.Error())
|
||||
return nil, &qerr.TransportError{
|
||||
FrameType: uint64(typeByte),
|
||||
ErrorCode: qerr.FrameEncodingError,
|
||||
ErrorMessage: err.Error(),
|
||||
}
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
|
|
@ -4,13 +4,14 @@ import (
|
|||
"bytes"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/qerr"
|
||||
"github.com/lucas-clemente/quic-go/quicvarint"
|
||||
)
|
||||
|
||||
// A ResetStreamFrame is a RESET_STREAM frame in QUIC
|
||||
type ResetStreamFrame struct {
|
||||
StreamID protocol.StreamID
|
||||
ErrorCode protocol.ApplicationErrorCode
|
||||
ErrorCode qerr.StreamErrorCode
|
||||
FinalSize protocol.ByteCount
|
||||
}
|
||||
|
||||
|
@ -38,7 +39,7 @@ func parseResetStreamFrame(r *bytes.Reader, _ protocol.VersionNumber) (*ResetStr
|
|||
|
||||
return &ResetStreamFrame{
|
||||
StreamID: streamID,
|
||||
ErrorCode: protocol.ApplicationErrorCode(errorCode),
|
||||
ErrorCode: qerr.StreamErrorCode(errorCode),
|
||||
FinalSize: byteOffset,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -4,13 +4,14 @@ import (
|
|||
"bytes"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/qerr"
|
||||
"github.com/lucas-clemente/quic-go/quicvarint"
|
||||
)
|
||||
|
||||
// A StopSendingFrame is a STOP_SENDING frame
|
||||
type StopSendingFrame struct {
|
||||
StreamID protocol.StreamID
|
||||
ErrorCode protocol.ApplicationErrorCode
|
||||
ErrorCode qerr.StreamErrorCode
|
||||
}
|
||||
|
||||
// parseStopSendingFrame parses a STOP_SENDING frame
|
||||
|
@ -30,7 +31,7 @@ func parseStopSendingFrame(r *bytes.Reader, _ protocol.VersionNumber) (*StopSend
|
|||
|
||||
return &StopSendingFrame{
|
||||
StreamID: protocol.StreamID(streamID),
|
||||
ErrorCode: protocol.ApplicationErrorCode(errorCode),
|
||||
ErrorCode: qerr.StreamErrorCode(errorCode),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"io"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/qerr"
|
||||
"github.com/lucas-clemente/quic-go/quicvarint"
|
||||
)
|
||||
|
||||
|
@ -79,7 +78,7 @@ func parseStreamFrame(r *bytes.Reader, _ protocol.VersionNumber) (*StreamFrame,
|
|||
}
|
||||
}
|
||||
if frame.Offset+frame.DataLen() > protocol.MaxByteCount {
|
||||
return nil, qerr.NewError(qerr.FrameEncodingError, "stream data overflows maximum offset")
|
||||
return nil, errors.New("stream data overflows maximum offset")
|
||||
}
|
||||
return frame, nil
|
||||
}
|
||||
|
|
|
@ -90,7 +90,10 @@ type TransportParameters struct {
|
|||
// Unmarshal the transport parameters
|
||||
func (p *TransportParameters) Unmarshal(data []byte, sentBy protocol.Perspective) error {
|
||||
if err := p.unmarshal(bytes.NewReader(data), sentBy, false); err != nil {
|
||||
return qerr.NewError(qerr.TransportParameterError, err.Error())
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.TransportParameterError,
|
||||
ErrorMessage: err.Error(),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -259,7 +262,7 @@ func (p *TransportParameters) readNumericTransportParameter(
|
|||
return fmt.Errorf("error while reading transport parameter %d: %s", paramID, err)
|
||||
}
|
||||
if remainingLen-r.Len() != expectedLen {
|
||||
return fmt.Errorf("inconsistent transport parameter length for %d", paramID)
|
||||
return fmt.Errorf("inconsistent transport parameter length for transport parameter %#x", paramID)
|
||||
}
|
||||
//nolint:exhaustive // This only covers the numeric transport parameters.
|
||||
switch paramID {
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
package logging
|
||||
|
||||
// A CloseReason is the reason why a QUIC connection is closed.
|
||||
// It falls in one of 4 categories:
|
||||
// 1. The application closed the connection (with an application-specific error code).
|
||||
// 2. The transport closed the connection with a transport-error code.
|
||||
// 3. The connection timed out, either during the handshake, or due to an idle timeout.
|
||||
// 4. A stateless reset was received.
|
||||
type CloseReason struct {
|
||||
remote bool
|
||||
applicationError *ApplicationError
|
||||
transportError *TransportError
|
||||
|
||||
timeout *TimeoutReason
|
||||
statelessResetToken *StatelessResetToken
|
||||
versions []VersionNumber
|
||||
}
|
||||
|
||||
// NewApplicationCloseReason creates a new CloseReason for an application error.
|
||||
func NewApplicationCloseReason(errorCode ApplicationError, remote bool) CloseReason {
|
||||
return CloseReason{remote: remote, applicationError: &errorCode}
|
||||
}
|
||||
|
||||
// NewTransportCloseReason creates a new CloseReason for a transport error.
|
||||
func NewTransportCloseReason(errorCode TransportError, remote bool) CloseReason {
|
||||
return CloseReason{remote: remote, transportError: &errorCode}
|
||||
}
|
||||
|
||||
// NewTimeoutCloseReason creates a new CloseReason for a connection timeout.
|
||||
func NewTimeoutCloseReason(r TimeoutReason) CloseReason {
|
||||
return CloseReason{timeout: &r}
|
||||
}
|
||||
|
||||
// NewStatelessResetCloseReason creates a new CloseReason for a stateless reset.
|
||||
func NewStatelessResetCloseReason(token StatelessResetToken) CloseReason {
|
||||
return CloseReason{statelessResetToken: &token}
|
||||
}
|
||||
|
||||
// NewVersionNegotiationError creates a new CloseReason for a version negotiation error.
|
||||
func NewVersionNegotiationError(versions []VersionNumber) CloseReason {
|
||||
return CloseReason{versions: versions}
|
||||
}
|
||||
|
||||
// ApplicationError gets the application error.
|
||||
func (r *CloseReason) ApplicationError() (errorCode ApplicationError, remote bool, ok bool) {
|
||||
if r.applicationError == nil {
|
||||
return
|
||||
}
|
||||
return *r.applicationError, r.remote, true
|
||||
}
|
||||
|
||||
// TransportError gets the transport error.
|
||||
func (r *CloseReason) TransportError() (errorCode TransportError, remote bool, ok bool) {
|
||||
if r.transportError == nil {
|
||||
return
|
||||
}
|
||||
return *r.transportError, r.remote, true
|
||||
}
|
||||
|
||||
// Timeout gets the timeout error.
|
||||
func (r *CloseReason) Timeout() (reason TimeoutReason, ok bool) {
|
||||
if r.timeout == nil {
|
||||
return
|
||||
}
|
||||
return *r.timeout, true
|
||||
}
|
||||
|
||||
// StatelessReset gets the stateless reset token.
|
||||
func (r *CloseReason) StatelessReset() (token StatelessResetToken, ok bool) {
|
||||
if r.statelessResetToken == nil {
|
||||
return
|
||||
}
|
||||
return *r.statelessResetToken, true
|
||||
}
|
||||
|
||||
func (r *CloseReason) VersionNegotiation() (versions []VersionNumber, ok bool) {
|
||||
return r.versions, len(r.versions) > 0
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
package logging
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
|
@ -49,9 +50,9 @@ type (
|
|||
PreferredAddress = wire.PreferredAddress
|
||||
|
||||
// A TransportError is a transport-level error code.
|
||||
TransportError = qerr.ErrorCode
|
||||
TransportError = qerr.TransportErrorCode
|
||||
// An ApplicationError is an application-defined error code.
|
||||
ApplicationError = qerr.ErrorCode
|
||||
ApplicationError = qerr.TransportErrorCode
|
||||
|
||||
// The RTTStats contain statistics used by the congestion controller.
|
||||
RTTStats = utils.RTTStats
|
||||
|
@ -91,11 +92,11 @@ const (
|
|||
|
||||
// A Tracer traces events.
|
||||
type Tracer interface {
|
||||
// ConnectionTracer requests a new tracer for a connection.
|
||||
// TracerForConnection requests a new tracer for a connection.
|
||||
// The ODCID is the original destination connection ID:
|
||||
// The destination connection ID that the client used on the first Initial packet it sent on this connection.
|
||||
// If nil is returned, tracing will be disabled for this connection.
|
||||
TracerForConnection(p Perspective, odcid ConnectionID) ConnectionTracer
|
||||
TracerForConnection(ctx context.Context, p Perspective, odcid ConnectionID) ConnectionTracer
|
||||
|
||||
SentPacket(net.Addr, *Header, ByteCount, []Frame)
|
||||
DroppedPacket(net.Addr, PacketType, ByteCount, PacketDropReason)
|
||||
|
@ -103,8 +104,9 @@ type Tracer interface {
|
|||
|
||||
// A ConnectionTracer records events.
|
||||
type ConnectionTracer interface {
|
||||
StartedConnection(local, remote net.Addr, version VersionNumber, srcConnID, destConnID ConnectionID)
|
||||
ClosedConnection(CloseReason)
|
||||
StartedConnection(local, remote net.Addr, srcConnID, destConnID ConnectionID)
|
||||
NegotiatedVersion(chosen VersionNumber, clientVersions, serverVersions []VersionNumber)
|
||||
ClosedConnection(error)
|
||||
SentTransportParameters(*TransportParameters)
|
||||
ReceivedTransportParameters(*TransportParameters)
|
||||
RestoredTransportParameters(parameters *TransportParameters) // for 0-RTT
|
||||
|
@ -115,6 +117,7 @@ type ConnectionTracer interface {
|
|||
BufferedPacket(PacketType)
|
||||
DroppedPacket(PacketType, ByteCount, PacketDropReason)
|
||||
UpdatedMetrics(rttStats *RTTStats, cwnd, bytesInFlight ByteCount, packetsInFlight int)
|
||||
AcknowledgedPacket(EncryptionLevel, PacketNumber)
|
||||
LostPacket(EncryptionLevel, PacketNumber, PacketLossReason)
|
||||
UpdatedCongestionState(CongestionState)
|
||||
UpdatedPTOCount(value uint32)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package logging
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
@ -22,10 +23,10 @@ func NewMultiplexedTracer(tracers ...Tracer) Tracer {
|
|||
return &tracerMultiplexer{tracers}
|
||||
}
|
||||
|
||||
func (m *tracerMultiplexer) TracerForConnection(p Perspective, odcid ConnectionID) ConnectionTracer {
|
||||
func (m *tracerMultiplexer) TracerForConnection(ctx context.Context, p Perspective, odcid ConnectionID) ConnectionTracer {
|
||||
var connTracers []ConnectionTracer
|
||||
for _, t := range m.tracers {
|
||||
if ct := t.TracerForConnection(p, odcid); ct != nil {
|
||||
if ct := t.TracerForConnection(ctx, p, odcid); ct != nil {
|
||||
connTracers = append(connTracers, ct)
|
||||
}
|
||||
}
|
||||
|
@ -61,15 +62,21 @@ func NewMultiplexedConnectionTracer(tracers ...ConnectionTracer) ConnectionTrace
|
|||
return &connTracerMultiplexer{tracers: tracers}
|
||||
}
|
||||
|
||||
func (m *connTracerMultiplexer) StartedConnection(local, remote net.Addr, version VersionNumber, srcConnID, destConnID ConnectionID) {
|
||||
func (m *connTracerMultiplexer) StartedConnection(local, remote net.Addr, srcConnID, destConnID ConnectionID) {
|
||||
for _, t := range m.tracers {
|
||||
t.StartedConnection(local, remote, version, srcConnID, destConnID)
|
||||
t.StartedConnection(local, remote, srcConnID, destConnID)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *connTracerMultiplexer) ClosedConnection(reason CloseReason) {
|
||||
func (m *connTracerMultiplexer) NegotiatedVersion(chosen VersionNumber, clientVersions, serverVersions []VersionNumber) {
|
||||
for _, t := range m.tracers {
|
||||
t.ClosedConnection(reason)
|
||||
t.NegotiatedVersion(chosen, clientVersions, serverVersions)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *connTracerMultiplexer) ClosedConnection(e error) {
|
||||
for _, t := range m.tracers {
|
||||
t.ClosedConnection(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,6 +146,12 @@ func (m *connTracerMultiplexer) UpdatedMetrics(rttStats *RTTStats, cwnd, bytesIn
|
|||
}
|
||||
}
|
||||
|
||||
func (m *connTracerMultiplexer) AcknowledgedPacket(encLevel EncryptionLevel, pn PacketNumber) {
|
||||
for _, t := range m.tracers {
|
||||
t.AcknowledgedPacket(encLevel, pn)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *connTracerMultiplexer) LostPacket(encLevel EncryptionLevel, pn PacketNumber, reason PacketLossReason) {
|
||||
for _, t := range m.tracers {
|
||||
t.LostPacket(encLevel, pn, reason)
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
package logging
|
||||
|
||||
import "github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
|
||||
// PacketType is the packet type of a QUIC packet
|
||||
type PacketType = protocol.PacketType
|
||||
type PacketType uint8
|
||||
|
||||
const (
|
||||
// PacketTypeInitial is the packet type of an Initial packet
|
||||
|
|
|
@ -22,5 +22,6 @@ package quic
|
|||
//go:generate sh -c "./mockgen_private.sh quic mock_unknown_packet_handler_test.go github.com/lucas-clemente/quic-go unknownPacketHandler"
|
||||
//go:generate sh -c "./mockgen_private.sh quic mock_packet_handler_manager_test.go github.com/lucas-clemente/quic-go packetHandlerManager"
|
||||
//go:generate sh -c "./mockgen_private.sh quic mock_multiplexer_test.go github.com/lucas-clemente/quic-go multiplexer"
|
||||
//go:generate sh -c "./mockgen_private.sh quic mock_batch_conn_test.go github.com/lucas-clemente/quic-go batchConn"
|
||||
//go:generate sh -c "mockgen -package quic -self_package github.com/lucas-clemente/quic-go -destination mock_token_store_test.go github.com/lucas-clemente/quic-go TokenStore && goimports -w mock_token_store_test.go"
|
||||
//go:generate sh -c "mockgen -package quic -self_package github.com/lucas-clemente/quic-go -destination mock_packetconn_test.go net PacketConn && goimports -w mock_packetconn_test.go"
|
||||
|
|
|
@ -18,14 +18,6 @@ import (
|
|||
"github.com/lucas-clemente/quic-go/logging"
|
||||
)
|
||||
|
||||
type statelessResetErr struct {
|
||||
token protocol.StatelessResetToken
|
||||
}
|
||||
|
||||
func (e statelessResetErr) Error() string {
|
||||
return fmt.Sprintf("received a stateless reset with token %x", e.token)
|
||||
}
|
||||
|
||||
type zeroRTTQueue struct {
|
||||
queue []*receivedPacket
|
||||
retireTimer *time.Timer
|
||||
|
@ -430,7 +422,7 @@ func (h *packetHandlerMap) maybeHandleStatelessReset(data []byte) bool {
|
|||
copy(token[:], data[len(data)-16:])
|
||||
if sess, ok := h.resetTokens[token]; ok {
|
||||
h.logger.Debugf("Received a stateless reset with token %#x. Closing session.", token)
|
||||
go sess.destroy(statelessResetErr{token: token})
|
||||
go sess.destroy(&StatelessResetError{Token: token})
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
|
@ -20,7 +20,8 @@ type packer interface {
|
|||
PackPacket() (*packedPacket, error)
|
||||
MaybePackProbePacket(protocol.EncryptionLevel) (*packedPacket, error)
|
||||
MaybePackAckPacket(handshakeConfirmed bool) (*packedPacket, error)
|
||||
PackConnectionClose(*qerr.QuicError) (*coalescedPacket, error)
|
||||
PackConnectionClose(*qerr.TransportError) (*coalescedPacket, error)
|
||||
PackApplicationClose(*qerr.ApplicationError) (*coalescedPacket, error)
|
||||
|
||||
SetMaxPacketSize(protocol.ByteCount)
|
||||
PackMTUProbePacket(ping ackhandler.Frame, size protocol.ByteCount) (*packedPacket, error)
|
||||
|
@ -203,14 +204,27 @@ func newPacketPacker(
|
|||
}
|
||||
}
|
||||
|
||||
// PackConnectionClose packs a packet that ONLY contains a ConnectionCloseFrame
|
||||
func (p *packetPacker) PackConnectionClose(quicErr *qerr.QuicError) (*coalescedPacket, error) {
|
||||
// PackConnectionClose packs a packet that closes the connection with a transport error.
|
||||
func (p *packetPacker) PackConnectionClose(e *qerr.TransportError) (*coalescedPacket, error) {
|
||||
var reason string
|
||||
// don't send details of crypto errors
|
||||
if !quicErr.IsCryptoError() {
|
||||
reason = quicErr.ErrorMessage
|
||||
if !e.ErrorCode.IsCryptoError() {
|
||||
reason = e.ErrorMessage
|
||||
}
|
||||
return p.packConnectionClose(false, uint64(e.ErrorCode), e.FrameType, reason)
|
||||
}
|
||||
|
||||
// PackApplicationClose packs a packet that closes the connection with an application error.
|
||||
func (p *packetPacker) PackApplicationClose(e *qerr.ApplicationError) (*coalescedPacket, error) {
|
||||
return p.packConnectionClose(true, uint64(e.ErrorCode), 0, e.ErrorMessage)
|
||||
}
|
||||
|
||||
func (p *packetPacker) packConnectionClose(
|
||||
isApplicationError bool,
|
||||
errorCode uint64,
|
||||
frameType uint64,
|
||||
reason string,
|
||||
) (*coalescedPacket, error) {
|
||||
var sealers [4]sealer
|
||||
var hdrs [4]*wire.ExtendedHeader
|
||||
var payloads [4]*payload
|
||||
|
@ -221,20 +235,17 @@ func (p *packetPacker) PackConnectionClose(quicErr *qerr.QuicError) (*coalescedP
|
|||
if p.perspective == protocol.PerspectiveServer && encLevel == protocol.Encryption0RTT {
|
||||
continue
|
||||
}
|
||||
quicErrToSend := quicErr
|
||||
reasonPhrase := reason
|
||||
if encLevel == protocol.EncryptionInitial || encLevel == protocol.EncryptionHandshake {
|
||||
// don't send application errors in Initial or Handshake packets
|
||||
if quicErr.IsApplicationError() {
|
||||
quicErrToSend = qerr.NewError(qerr.ApplicationError, "")
|
||||
reasonPhrase = ""
|
||||
}
|
||||
}
|
||||
ccf := &wire.ConnectionCloseFrame{
|
||||
IsApplicationError: quicErrToSend.IsApplicationError(),
|
||||
ErrorCode: quicErrToSend.ErrorCode,
|
||||
FrameType: quicErrToSend.FrameType,
|
||||
ReasonPhrase: reasonPhrase,
|
||||
IsApplicationError: isApplicationError,
|
||||
ErrorCode: errorCode,
|
||||
FrameType: frameType,
|
||||
ReasonPhrase: reason,
|
||||
}
|
||||
// don't send application errors in Initial or Handshake packets
|
||||
if isApplicationError && (encLevel == protocol.EncryptionInitial || encLevel == protocol.EncryptionHandshake) {
|
||||
ccf.IsApplicationError = false
|
||||
ccf.ErrorCode = uint64(qerr.ApplicationErrorErrorCode)
|
||||
ccf.ReasonPhrase = ""
|
||||
}
|
||||
payload := &payload{
|
||||
frames: []ackhandler.Frame{{Frame: ccf}},
|
||||
|
|
|
@ -18,11 +18,6 @@ type headerParseError struct {
|
|||
err error
|
||||
}
|
||||
|
||||
func (e *headerParseError) Is(err error) bool {
|
||||
_, ok := err.(*headerParseError)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (e *headerParseError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
package quicvarint
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Reader implements both the io.ByteReader and io.Reader interfaces.
|
||||
type Reader interface {
|
||||
io.ByteReader
|
||||
io.Reader
|
||||
}
|
||||
|
||||
var _ Reader = &bytes.Reader{}
|
||||
|
||||
type byteReader struct {
|
||||
io.Reader
|
||||
}
|
||||
|
||||
var _ Reader = &byteReader{}
|
||||
|
||||
// NewReader returns a Reader for r.
|
||||
// If r already implements both io.ByteReader and io.Reader, NewReader returns r.
|
||||
// Otherwise, r is wrapped to add the missing interfaces.
|
||||
func NewReader(r io.Reader) Reader {
|
||||
if r, ok := r.(Reader); ok {
|
||||
return r
|
||||
}
|
||||
return &byteReader{r}
|
||||
}
|
||||
|
||||
func (r *byteReader) ReadByte() (byte, error) {
|
||||
var b [1]byte
|
||||
_, err := r.Reader.Read(b[:])
|
||||
return b[0], err
|
||||
}
|
||||
|
||||
// Writer implements both the io.ByteWriter and io.Writer interfaces.
|
||||
type Writer interface {
|
||||
io.ByteWriter
|
||||
io.Writer
|
||||
}
|
||||
|
||||
var _ Writer = &bytes.Buffer{}
|
||||
|
||||
type byteWriter struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
var _ Writer = &byteWriter{}
|
||||
|
||||
// NewWriter returns a Writer for w.
|
||||
// If r already implements both io.ByteWriter and io.Writer, NewWriter returns w.
|
||||
// Otherwise, w is wrapped to add the missing interfaces.
|
||||
func NewWriter(w io.Writer) Writer {
|
||||
if w, ok := w.(Writer); ok {
|
||||
return w
|
||||
}
|
||||
return &byteWriter{w}
|
||||
}
|
||||
|
||||
func (w *byteWriter) WriteByte(c byte) error {
|
||||
_, err := w.Writer.Write([]byte{c})
|
||||
return err
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package quicvarint
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
|
@ -10,15 +9,21 @@ import (
|
|||
|
||||
// taken from the QUIC draft
|
||||
const (
|
||||
// Min is the minimum value allowed for a QUIC varint.
|
||||
Min = 0
|
||||
|
||||
// Max is the maximum allowed value for a QUIC varint (2^62-1).
|
||||
Max = maxVarInt8
|
||||
|
||||
maxVarInt1 = 63
|
||||
maxVarInt2 = 16383
|
||||
maxVarInt4 = 1073741823
|
||||
maxVarInt8 = 4611686018427387903
|
||||
)
|
||||
|
||||
// Read reads a number in the QUIC varint format
|
||||
func Read(b io.ByteReader) (uint64, error) {
|
||||
firstByte, err := b.ReadByte()
|
||||
// Read reads a number in the QUIC varint format from r.
|
||||
func Read(r io.ByteReader) (uint64, error) {
|
||||
firstByte, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
@ -28,53 +33,53 @@ func Read(b io.ByteReader) (uint64, error) {
|
|||
if len == 1 {
|
||||
return uint64(b1), nil
|
||||
}
|
||||
b2, err := b.ReadByte()
|
||||
b2, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len == 2 {
|
||||
return uint64(b2) + uint64(b1)<<8, nil
|
||||
}
|
||||
b3, err := b.ReadByte()
|
||||
b3, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
b4, err := b.ReadByte()
|
||||
b4, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len == 4 {
|
||||
return uint64(b4) + uint64(b3)<<8 + uint64(b2)<<16 + uint64(b1)<<24, nil
|
||||
}
|
||||
b5, err := b.ReadByte()
|
||||
b5, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
b6, err := b.ReadByte()
|
||||
b6, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
b7, err := b.ReadByte()
|
||||
b7, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
b8, err := b.ReadByte()
|
||||
b8, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint64(b8) + uint64(b7)<<8 + uint64(b6)<<16 + uint64(b5)<<24 + uint64(b4)<<32 + uint64(b3)<<40 + uint64(b2)<<48 + uint64(b1)<<56, nil
|
||||
}
|
||||
|
||||
// Write writes a number in the QUIC varint format
|
||||
func Write(b *bytes.Buffer, i uint64) {
|
||||
// Write writes i in the QUIC varint format to w.
|
||||
func Write(w Writer, i uint64) {
|
||||
if i <= maxVarInt1 {
|
||||
b.WriteByte(uint8(i))
|
||||
w.WriteByte(uint8(i))
|
||||
} else if i <= maxVarInt2 {
|
||||
b.Write([]byte{uint8(i>>8) | 0x40, uint8(i)})
|
||||
w.Write([]byte{uint8(i>>8) | 0x40, uint8(i)})
|
||||
} else if i <= maxVarInt4 {
|
||||
b.Write([]byte{uint8(i>>24) | 0x80, uint8(i >> 16), uint8(i >> 8), uint8(i)})
|
||||
w.Write([]byte{uint8(i>>24) | 0x80, uint8(i >> 16), uint8(i >> 8), uint8(i)})
|
||||
} else if i <= maxVarInt8 {
|
||||
b.Write([]byte{
|
||||
w.Write([]byte{
|
||||
uint8(i>>56) | 0xc0, uint8(i >> 48), uint8(i >> 40), uint8(i >> 32),
|
||||
uint8(i >> 24), uint8(i >> 16), uint8(i >> 8), uint8(i),
|
||||
})
|
||||
|
@ -83,35 +88,35 @@ func Write(b *bytes.Buffer, i uint64) {
|
|||
}
|
||||
}
|
||||
|
||||
// WriteWithLen writes a number in the QUIC varint format, with the desired length.
|
||||
func WriteWithLen(b *bytes.Buffer, i uint64, length protocol.ByteCount) {
|
||||
// WriteWithLen writes i in the QUIC varint format with the desired length to w.
|
||||
func WriteWithLen(w Writer, i uint64, length protocol.ByteCount) {
|
||||
if length != 1 && length != 2 && length != 4 && length != 8 {
|
||||
panic("invalid varint length")
|
||||
}
|
||||
l := Len(i)
|
||||
if l == length {
|
||||
Write(b, i)
|
||||
Write(w, i)
|
||||
return
|
||||
}
|
||||
if l > length {
|
||||
panic(fmt.Sprintf("cannot encode %d in %d bytes", i, length))
|
||||
}
|
||||
if length == 2 {
|
||||
b.WriteByte(0b01000000)
|
||||
w.WriteByte(0b01000000)
|
||||
} else if length == 4 {
|
||||
b.WriteByte(0b10000000)
|
||||
w.WriteByte(0b10000000)
|
||||
} else if length == 8 {
|
||||
b.WriteByte(0b11000000)
|
||||
w.WriteByte(0b11000000)
|
||||
}
|
||||
for j := protocol.ByteCount(1); j < length-l; j++ {
|
||||
b.WriteByte(0)
|
||||
w.WriteByte(0)
|
||||
}
|
||||
for j := protocol.ByteCount(0); j < l; j++ {
|
||||
b.WriteByte(uint8(i >> (8 * (l - 1 - j))))
|
||||
w.WriteByte(uint8(i >> (8 * (l - 1 - j))))
|
||||
}
|
||||
}
|
||||
|
||||
// Len determines the number of bytes that will be needed to write a number
|
||||
// Len determines the number of bytes that will be needed to write the number i.
|
||||
func Len(i uint64) protocol.ByteCount {
|
||||
if i <= maxVarInt1 {
|
||||
return 1
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
"github.com/lucas-clemente/quic-go/internal/flowcontrol"
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/qerr"
|
||||
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||
"github.com/lucas-clemente/quic-go/internal/wire"
|
||||
)
|
||||
|
@ -38,7 +39,7 @@ type receiveStream struct {
|
|||
|
||||
closeForShutdownErr error
|
||||
cancelReadErr error
|
||||
resetRemotelyErr StreamError
|
||||
resetRemotelyErr *StreamError
|
||||
|
||||
closedForShutdown bool // set when CloseForShutdown() is called
|
||||
finRead bool // set once we read a frame with a Fin
|
||||
|
@ -196,7 +197,7 @@ func (s *receiveStream) dequeueNextFrame() {
|
|||
s.readPosInFrame = 0
|
||||
}
|
||||
|
||||
func (s *receiveStream) CancelRead(errorCode protocol.ApplicationErrorCode) {
|
||||
func (s *receiveStream) CancelRead(errorCode StreamErrorCode) {
|
||||
s.mutex.Lock()
|
||||
completed := s.cancelReadImpl(errorCode)
|
||||
s.mutex.Unlock()
|
||||
|
@ -207,7 +208,7 @@ func (s *receiveStream) CancelRead(errorCode protocol.ApplicationErrorCode) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *receiveStream) cancelReadImpl(errorCode protocol.ApplicationErrorCode) bool /* completed */ {
|
||||
func (s *receiveStream) cancelReadImpl(errorCode qerr.StreamErrorCode) bool /* completed */ {
|
||||
if s.finRead || s.canceledRead || s.resetRemotely {
|
||||
return false
|
||||
}
|
||||
|
@ -281,9 +282,9 @@ func (s *receiveStream) handleResetStreamFrameImpl(frame *wire.ResetStreamFrame)
|
|||
return false, nil
|
||||
}
|
||||
s.resetRemotely = true
|
||||
s.resetRemotelyErr = streamCanceledError{
|
||||
errorCode: frame.ErrorCode,
|
||||
error: fmt.Errorf("stream %d was reset with error code %d", s.streamID, frame.ErrorCode),
|
||||
s.resetRemotelyErr = &StreamError{
|
||||
StreamID: s.streamID,
|
||||
ErrorCode: frame.ErrorCode,
|
||||
}
|
||||
s.signalRead()
|
||||
return newlyRcvdFinalOffset, nil
|
||||
|
|
|
@ -7,9 +7,9 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/ackhandler"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/flowcontrol"
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/qerr"
|
||||
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||
"github.com/lucas-clemente/quic-go/internal/wire"
|
||||
)
|
||||
|
@ -407,12 +407,12 @@ func (s *sendStream) Close() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *sendStream) CancelWrite(errorCode protocol.ApplicationErrorCode) {
|
||||
func (s *sendStream) CancelWrite(errorCode StreamErrorCode) {
|
||||
s.cancelWriteImpl(errorCode, fmt.Errorf("Write on stream %d canceled with error code %d", s.streamID, errorCode))
|
||||
}
|
||||
|
||||
// must be called after locking the mutex
|
||||
func (s *sendStream) cancelWriteImpl(errorCode protocol.ApplicationErrorCode, writeErr error) {
|
||||
func (s *sendStream) cancelWriteImpl(errorCode qerr.StreamErrorCode, writeErr error) {
|
||||
s.mutex.Lock()
|
||||
if s.canceledWrite {
|
||||
s.mutex.Unlock()
|
||||
|
@ -449,11 +449,10 @@ func (s *sendStream) updateSendWindow(limit protocol.ByteCount) {
|
|||
}
|
||||
|
||||
func (s *sendStream) handleStopSendingFrame(frame *wire.StopSendingFrame) {
|
||||
writeErr := streamCanceledError{
|
||||
errorCode: frame.ErrorCode,
|
||||
error: fmt.Errorf("stream %d was reset with error code %d", s.streamID, frame.ErrorCode),
|
||||
}
|
||||
s.cancelWriteImpl(frame.ErrorCode, writeErr)
|
||||
s.cancelWriteImpl(frame.ErrorCode, &StreamError{
|
||||
StreamID: s.streamID,
|
||||
ErrorCode: frame.ErrorCode,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *sendStream) Context() context.Context {
|
||||
|
|
|
@ -87,6 +87,7 @@ type baseServer struct {
|
|||
*handshake.TokenGenerator,
|
||||
bool, /* enable 0-RTT */
|
||||
logging.ConnectionTracer,
|
||||
uint64,
|
||||
utils.Logger,
|
||||
protocol.VersionNumber,
|
||||
) quicSession
|
||||
|
@ -365,7 +366,9 @@ func (s *baseServer) handlePacketImpl(p *receivedPacket) bool /* is the buffer s
|
|||
}
|
||||
return false
|
||||
}
|
||||
if !s.config.DisableVersionNegotiationPackets {
|
||||
go s.sendVersionNegotiationPacket(p, hdr)
|
||||
}
|
||||
return false
|
||||
}
|
||||
if hdr.IsLongHeader && hdr.Type != protocol.PacketTypeInitial {
|
||||
|
@ -450,6 +453,7 @@ func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) erro
|
|||
}
|
||||
s.logger.Debugf("Changing connection ID to %s.", connID)
|
||||
var sess quicSession
|
||||
tracingID := nextSessionTracingID()
|
||||
if added := s.sessionHandler.AddWithConnID(hdr.DestConnectionID, connID, func() packetHandler {
|
||||
var tracer logging.ConnectionTracer
|
||||
if s.config.Tracer != nil {
|
||||
|
@ -458,7 +462,11 @@ func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) erro
|
|||
if origDestConnID.Len() > 0 {
|
||||
connID = origDestConnID
|
||||
}
|
||||
tracer = s.config.Tracer.TracerForConnection(protocol.PerspectiveServer, connID)
|
||||
tracer = s.config.Tracer.TracerForConnection(
|
||||
context.WithValue(context.Background(), SessionTracingKey, tracingID),
|
||||
protocol.PerspectiveServer,
|
||||
connID,
|
||||
)
|
||||
}
|
||||
sess = s.newSession(
|
||||
newSendConn(s.conn, p.remoteAddr, p.info),
|
||||
|
@ -474,6 +482,7 @@ func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) erro
|
|||
s.tokenGenerator,
|
||||
s.acceptEarlySessions,
|
||||
tracer,
|
||||
tracingID,
|
||||
s.logger,
|
||||
hdr.Version,
|
||||
)
|
||||
|
@ -593,12 +602,12 @@ func (s *baseServer) sendConnectionRefused(remoteAddr net.Addr, hdr *wire.Header
|
|||
}
|
||||
|
||||
// sendError sends the error as a response to the packet received with header hdr
|
||||
func (s *baseServer) sendError(remoteAddr net.Addr, hdr *wire.Header, sealer handshake.LongHeaderSealer, errorCode qerr.ErrorCode, info *packetInfo) error {
|
||||
func (s *baseServer) sendError(remoteAddr net.Addr, hdr *wire.Header, sealer handshake.LongHeaderSealer, errorCode qerr.TransportErrorCode, info *packetInfo) error {
|
||||
packetBuffer := getPacketBuffer()
|
||||
defer packetBuffer.Release()
|
||||
buf := bytes.NewBuffer(packetBuffer.Data)
|
||||
|
||||
ccf := &wire.ConnectionCloseFrame{ErrorCode: errorCode}
|
||||
ccf := &wire.ConnectionCloseFrame{ErrorCode: uint64(errorCode)}
|
||||
|
||||
replyHdr := &wire.ExtendedHeader{}
|
||||
replyHdr.IsLongHeader = true
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"net"
|
||||
"reflect"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/ackhandler"
|
||||
|
@ -122,23 +123,12 @@ type errCloseForRecreating struct {
|
|||
nextVersion protocol.VersionNumber
|
||||
}
|
||||
|
||||
func (errCloseForRecreating) Error() string {
|
||||
func (e *errCloseForRecreating) Error() string {
|
||||
return "closing session in order to recreate it"
|
||||
}
|
||||
|
||||
func (errCloseForRecreating) Is(target error) bool {
|
||||
_, ok := target.(errCloseForRecreating)
|
||||
return ok
|
||||
}
|
||||
|
||||
type errVersionNegotiation struct {
|
||||
ourVersions []protocol.VersionNumber
|
||||
theirVersions []protocol.VersionNumber
|
||||
}
|
||||
|
||||
func (e errVersionNegotiation) Error() string {
|
||||
return fmt.Sprintf("no compatible QUIC version found (we support %s, server offered %s)", e.ourVersions, e.theirVersions)
|
||||
}
|
||||
var sessionTracingID uint64 // to be accessed atomically
|
||||
func nextSessionTracingID() uint64 { return atomic.AddUint64(&sessionTracingID, 1) }
|
||||
|
||||
// A Session is a QUIC session
|
||||
type session struct {
|
||||
|
@ -252,6 +242,7 @@ var newSession = func(
|
|||
tokenGenerator *handshake.TokenGenerator,
|
||||
enable0RTT bool,
|
||||
tracer logging.ConnectionTracer,
|
||||
tracingID uint64,
|
||||
logger utils.Logger,
|
||||
v protocol.VersionNumber,
|
||||
) quicSession {
|
||||
|
@ -291,8 +282,10 @@ var newSession = func(
|
|||
s.version,
|
||||
)
|
||||
s.preSetup()
|
||||
s.ctx, s.ctxCancel = context.WithCancel(context.WithValue(context.Background(), SessionTracingKey, tracingID))
|
||||
s.sentPacketHandler, s.receivedPacketHandler = ackhandler.NewAckHandler(
|
||||
0,
|
||||
getMaxPacketSize(s.conn.RemoteAddr()),
|
||||
s.rttStats,
|
||||
s.perspective,
|
||||
s.tracer,
|
||||
|
@ -380,6 +373,7 @@ var newClientSession = func(
|
|||
enable0RTT bool,
|
||||
hasNegotiatedVersion bool,
|
||||
tracer logging.ConnectionTracer,
|
||||
tracingID uint64,
|
||||
logger utils.Logger,
|
||||
v protocol.VersionNumber,
|
||||
) quicSession {
|
||||
|
@ -415,8 +409,10 @@ var newClientSession = func(
|
|||
s.version,
|
||||
)
|
||||
s.preSetup()
|
||||
s.ctx, s.ctxCancel = context.WithCancel(context.WithValue(context.Background(), SessionTracingKey, tracingID))
|
||||
s.sentPacketHandler, s.receivedPacketHandler = ackhandler.NewAckHandler(
|
||||
initialPacketNumber,
|
||||
getMaxPacketSize(s.conn.RemoteAddr()),
|
||||
s.rttStats,
|
||||
s.perspective,
|
||||
s.tracer,
|
||||
|
@ -522,7 +518,6 @@ func (s *session) preSetup() {
|
|||
s.receivedPackets = make(chan *receivedPacket, protocol.MaxSessionUnprocessedPackets)
|
||||
s.closeChan = make(chan closeError, 1)
|
||||
s.sendingScheduled = make(chan struct{}, 1)
|
||||
s.ctx, s.ctxCancel = context.WithCancel(context.Background())
|
||||
s.handshakeCtx, s.handshakeCtxCancel = context.WithCancel(context.Background())
|
||||
|
||||
now := time.Now()
|
||||
|
@ -608,7 +603,6 @@ runLoop:
|
|||
// nothing to see here.
|
||||
case <-sendQueueAvailable:
|
||||
case firstPacket := <-s.receivedPackets:
|
||||
s.sentPacketHandler.ReceivedBytes(firstPacket.Size())
|
||||
wasProcessed := s.handlePacketImpl(firstPacket)
|
||||
// Don't set timers and send packets if the packet made us close the session.
|
||||
select {
|
||||
|
@ -664,19 +658,13 @@ runLoop:
|
|||
s.framer.QueueControlFrame(&wire.PingFrame{})
|
||||
s.keepAlivePingSent = true
|
||||
} else if !s.handshakeComplete && now.Sub(s.sessionCreationTime) >= s.config.handshakeTimeout() {
|
||||
if s.tracer != nil {
|
||||
s.tracer.ClosedConnection(logging.NewTimeoutCloseReason(logging.TimeoutReasonHandshake))
|
||||
}
|
||||
s.destroyImpl(qerr.NewTimeoutError("Handshake did not complete in time"))
|
||||
s.destroyImpl(qerr.ErrHandshakeTimeout)
|
||||
continue
|
||||
} else {
|
||||
idleTimeoutStartTime := s.idleTimeoutStartTime()
|
||||
if (!s.handshakeComplete && now.Sub(idleTimeoutStartTime) >= s.config.HandshakeIdleTimeout) ||
|
||||
(s.handshakeComplete && now.Sub(idleTimeoutStartTime) >= s.idleTimeout) {
|
||||
if s.tracer != nil {
|
||||
s.tracer.ClosedConnection(logging.NewTimeoutCloseReason(logging.TimeoutReasonIdle))
|
||||
}
|
||||
s.destroyImpl(qerr.NewTimeoutError("No recent network activity"))
|
||||
s.destroyImpl(qerr.ErrIdleTimeout)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
@ -697,8 +685,8 @@ runLoop:
|
|||
}
|
||||
}
|
||||
|
||||
s.handleCloseError(closeErr)
|
||||
if !errors.Is(closeErr.err, errCloseForRecreating{}) && s.tracer != nil {
|
||||
s.handleCloseError(&closeErr)
|
||||
if e := (&errCloseForRecreating{}); !errors.As(closeErr.err, &e) && s.tracer != nil {
|
||||
s.tracer.Close()
|
||||
}
|
||||
s.logger.Infof("Connection %s closed.", s.logID)
|
||||
|
@ -738,25 +726,28 @@ func (s *session) nextKeepAliveTime() time.Time {
|
|||
if !s.config.KeepAlive || s.keepAlivePingSent || !s.firstAckElicitingPacketAfterIdleSentTime.IsZero() {
|
||||
return time.Time{}
|
||||
}
|
||||
return s.lastPacketReceivedTime.Add(s.keepAliveInterval / 2)
|
||||
return s.lastPacketReceivedTime.Add(s.keepAliveInterval)
|
||||
}
|
||||
|
||||
func (s *session) maybeResetTimer() {
|
||||
var deadline time.Time
|
||||
if !s.handshakeComplete {
|
||||
deadline = s.sessionCreationTime.Add(utils.MinDuration(s.config.handshakeTimeout(), s.config.HandshakeIdleTimeout))
|
||||
deadline = utils.MinTime(
|
||||
s.sessionCreationTime.Add(s.config.handshakeTimeout()),
|
||||
s.idleTimeoutStartTime().Add(s.config.HandshakeIdleTimeout),
|
||||
)
|
||||
} else {
|
||||
if keepAliveTime := s.nextKeepAliveTime(); !keepAliveTime.IsZero() {
|
||||
deadline = keepAliveTime
|
||||
} else {
|
||||
deadline = s.idleTimeoutStartTime().Add(s.idleTimeout)
|
||||
}
|
||||
if !s.config.DisablePathMTUDiscovery {
|
||||
}
|
||||
if s.handshakeConfirmed && !s.config.DisablePathMTUDiscovery {
|
||||
if probeTime := s.mtuDiscoverer.NextProbeTime(); !probeTime.IsZero() {
|
||||
deadline = utils.MinTime(deadline, probeTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ackAlarm := s.receivedPacketHandler.GetAlarmTimeout(); !ackAlarm.IsZero() {
|
||||
deadline = utils.MinTime(deadline, ackAlarm)
|
||||
|
@ -786,6 +777,36 @@ func (s *session) handleHandshakeComplete() {
|
|||
s.connIDManager.SetHandshakeComplete()
|
||||
s.connIDGenerator.SetHandshakeComplete()
|
||||
|
||||
if s.perspective == protocol.PerspectiveClient {
|
||||
s.applyTransportParameters()
|
||||
return
|
||||
}
|
||||
|
||||
s.handleHandshakeConfirmed()
|
||||
|
||||
ticket, err := s.cryptoStreamHandler.GetSessionTicket()
|
||||
if err != nil {
|
||||
s.closeLocal(err)
|
||||
}
|
||||
if ticket != nil {
|
||||
s.oneRTTStream.Write(ticket)
|
||||
for s.oneRTTStream.HasData() {
|
||||
s.queueControlFrame(s.oneRTTStream.PopCryptoFrame(protocol.MaxPostHandshakeCryptoFrameSize))
|
||||
}
|
||||
}
|
||||
token, err := s.tokenGenerator.NewToken(s.conn.RemoteAddr())
|
||||
if err != nil {
|
||||
s.closeLocal(err)
|
||||
}
|
||||
s.queueControlFrame(&wire.NewTokenFrame{Token: token})
|
||||
s.queueControlFrame(&wire.HandshakeDoneFrame{})
|
||||
}
|
||||
|
||||
func (s *session) handleHandshakeConfirmed() {
|
||||
s.handshakeConfirmed = true
|
||||
s.sentPacketHandler.SetHandshakeConfirmed()
|
||||
s.cryptoStreamHandler.SetHandshakeConfirmed()
|
||||
|
||||
if !s.config.DisablePathMTUDiscovery {
|
||||
maxPacketSize := s.peerParams.MaxUDPPayloadSize
|
||||
if maxPacketSize == 0 {
|
||||
|
@ -802,34 +823,11 @@ func (s *session) handleHandshakeComplete() {
|
|||
},
|
||||
)
|
||||
}
|
||||
|
||||
if s.perspective == protocol.PerspectiveClient {
|
||||
s.applyTransportParameters()
|
||||
return
|
||||
}
|
||||
|
||||
s.handshakeConfirmed = true
|
||||
s.sentPacketHandler.SetHandshakeConfirmed()
|
||||
ticket, err := s.cryptoStreamHandler.GetSessionTicket()
|
||||
if err != nil {
|
||||
s.closeLocal(err)
|
||||
}
|
||||
if ticket != nil {
|
||||
s.oneRTTStream.Write(ticket)
|
||||
for s.oneRTTStream.HasData() {
|
||||
s.queueControlFrame(s.oneRTTStream.PopCryptoFrame(protocol.MaxPostHandshakeCryptoFrameSize))
|
||||
}
|
||||
}
|
||||
token, err := s.tokenGenerator.NewToken(s.conn.RemoteAddr())
|
||||
if err != nil {
|
||||
s.closeLocal(err)
|
||||
}
|
||||
s.queueControlFrame(&wire.NewTokenFrame{Token: token})
|
||||
s.cryptoStreamHandler.SetHandshakeConfirmed()
|
||||
s.queueControlFrame(&wire.HandshakeDoneFrame{})
|
||||
}
|
||||
|
||||
func (s *session) handlePacketImpl(rp *receivedPacket) bool {
|
||||
s.sentPacketHandler.ReceivedBytes(rp.Size())
|
||||
|
||||
if wire.IsVersionNegotiationPacket(rp.data) {
|
||||
s.handleVersionNegotiationPacket(rp)
|
||||
return false
|
||||
|
@ -940,7 +938,10 @@ func (s *session) handleSinglePacket(p *receivedPacket, hdr *wire.Header) bool /
|
|||
wasQueued = true
|
||||
s.tryQueueingUndecryptablePacket(p, hdr)
|
||||
case wire.ErrInvalidReservedBits:
|
||||
s.closeLocal(qerr.NewError(qerr.ProtocolViolation, err.Error()))
|
||||
s.closeLocal(&qerr.TransportError{
|
||||
ErrorCode: qerr.ProtocolViolation,
|
||||
ErrorMessage: err.Error(),
|
||||
})
|
||||
case handshake.ErrDecryptionFailed:
|
||||
// This might be a packet injected by an attacker. Drop it.
|
||||
if s.tracer != nil {
|
||||
|
@ -1081,13 +1082,16 @@ func (s *session) handleVersionNegotiationPacket(p *receivedPacket) {
|
|||
}
|
||||
newVersion, ok := protocol.ChooseSupportedVersion(s.config.Versions, supportedVersions)
|
||||
if !ok {
|
||||
s.destroyImpl(errVersionNegotiation{
|
||||
ourVersions: s.config.Versions,
|
||||
theirVersions: supportedVersions,
|
||||
s.destroyImpl(&VersionNegotiationError{
|
||||
Ours: s.config.Versions,
|
||||
Theirs: supportedVersions,
|
||||
})
|
||||
s.logger.Infof("No compatible QUIC version found.")
|
||||
return
|
||||
}
|
||||
if s.tracer != nil {
|
||||
s.tracer.NegotiatedVersion(newVersion, s.config.Versions, supportedVersions)
|
||||
}
|
||||
|
||||
s.logger.Infof("Switching to QUIC version %s.", newVersion)
|
||||
nextPN, _ := s.sentPacketHandler.PeekPacketNumber(protocol.EncryptionInitial)
|
||||
|
@ -1104,11 +1108,24 @@ func (s *session) handleUnpackedPacket(
|
|||
packetSize protocol.ByteCount, // only for logging
|
||||
) error {
|
||||
if len(packet.data) == 0 {
|
||||
return qerr.NewError(qerr.ProtocolViolation, "empty packet")
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.ProtocolViolation,
|
||||
ErrorMessage: "empty packet",
|
||||
}
|
||||
}
|
||||
|
||||
if !s.receivedFirstPacket {
|
||||
s.receivedFirstPacket = true
|
||||
if !s.versionNegotiated && s.tracer != nil {
|
||||
var clientVersions, serverVersions []protocol.VersionNumber
|
||||
switch s.perspective {
|
||||
case protocol.PerspectiveClient:
|
||||
clientVersions = s.config.Versions
|
||||
case protocol.PerspectiveServer:
|
||||
serverVersions = s.config.Versions
|
||||
}
|
||||
s.tracer.NegotiatedVersion(s.version, clientVersions, serverVersions)
|
||||
}
|
||||
// The server can change the source connection ID with the first Handshake packet.
|
||||
if s.perspective == protocol.PerspectiveClient && packet.hdr.IsLongHeader && !packet.hdr.SrcConnectionID.Equal(s.handshakeDestConnID) {
|
||||
cid := packet.hdr.SrcConnectionID
|
||||
|
@ -1130,7 +1147,6 @@ func (s *session) handleUnpackedPacket(
|
|||
s.tracer.StartedConnection(
|
||||
s.conn.LocalAddr(),
|
||||
s.conn.RemoteAddr(),
|
||||
s.version,
|
||||
packet.hdr.SrcConnectionID,
|
||||
packet.hdr.DestConnectionID,
|
||||
)
|
||||
|
@ -1246,13 +1262,20 @@ func (s *session) handlePacket(p *receivedPacket) {
|
|||
}
|
||||
|
||||
func (s *session) handleConnectionCloseFrame(frame *wire.ConnectionCloseFrame) {
|
||||
var e error
|
||||
if frame.IsApplicationError {
|
||||
e = qerr.NewApplicationError(frame.ErrorCode, frame.ReasonPhrase)
|
||||
} else {
|
||||
e = qerr.NewError(frame.ErrorCode, frame.ReasonPhrase)
|
||||
s.closeRemote(&qerr.ApplicationError{
|
||||
Remote: true,
|
||||
ErrorCode: qerr.ApplicationErrorCode(frame.ErrorCode),
|
||||
ErrorMessage: frame.ReasonPhrase,
|
||||
})
|
||||
return
|
||||
}
|
||||
s.closeRemote(e)
|
||||
s.closeRemote(&qerr.TransportError{
|
||||
Remote: true,
|
||||
ErrorCode: qerr.TransportErrorCode(frame.ErrorCode),
|
||||
FrameType: frame.FrameType,
|
||||
ErrorMessage: frame.ReasonPhrase,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *session) handleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel) error {
|
||||
|
@ -1333,7 +1356,10 @@ func (s *session) handlePathChallengeFrame(frame *wire.PathChallengeFrame) {
|
|||
|
||||
func (s *session) handleNewTokenFrame(frame *wire.NewTokenFrame) error {
|
||||
if s.perspective == protocol.PerspectiveServer {
|
||||
return qerr.NewError(qerr.ProtocolViolation, "Received NEW_TOKEN frame from the client.")
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.ProtocolViolation,
|
||||
ErrorMessage: "received NEW_TOKEN frame from the client",
|
||||
}
|
||||
}
|
||||
if s.config.TokenStore != nil {
|
||||
s.config.TokenStore.Put(s.tokenStoreKey, &ClientToken{data: frame.Token})
|
||||
|
@ -1351,27 +1377,37 @@ func (s *session) handleRetireConnectionIDFrame(f *wire.RetireConnectionIDFrame,
|
|||
|
||||
func (s *session) handleHandshakeDoneFrame() error {
|
||||
if s.perspective == protocol.PerspectiveServer {
|
||||
return qerr.NewError(qerr.ProtocolViolation, "received a HANDSHAKE_DONE frame")
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.ProtocolViolation,
|
||||
ErrorMessage: "received a HANDSHAKE_DONE frame",
|
||||
}
|
||||
}
|
||||
if !s.handshakeConfirmed {
|
||||
s.handleHandshakeConfirmed()
|
||||
}
|
||||
s.handshakeConfirmed = true
|
||||
s.sentPacketHandler.SetHandshakeConfirmed()
|
||||
s.cryptoStreamHandler.SetHandshakeConfirmed()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *session) handleAckFrame(frame *wire.AckFrame, encLevel protocol.EncryptionLevel) error {
|
||||
if err := s.sentPacketHandler.ReceivedAck(frame, encLevel, s.lastPacketReceivedTime); err != nil {
|
||||
acked1RTTPacket, err := s.sentPacketHandler.ReceivedAck(frame, encLevel, s.lastPacketReceivedTime)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if encLevel != protocol.Encryption1RTT {
|
||||
if !acked1RTTPacket {
|
||||
return nil
|
||||
}
|
||||
if s.perspective == protocol.PerspectiveClient && !s.handshakeConfirmed {
|
||||
s.handleHandshakeConfirmed()
|
||||
}
|
||||
return s.cryptoStreamHandler.SetLargest1RTTAcked(frame.LargestAcked())
|
||||
}
|
||||
|
||||
func (s *session) handleDatagramFrame(f *wire.DatagramFrame) error {
|
||||
if f.Length(s.version) > protocol.MaxDatagramFrameSize {
|
||||
return qerr.NewError(qerr.ProtocolViolation, "DATAGRAM frame too large")
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.ProtocolViolation,
|
||||
ErrorMessage: "DATAGRAM frame too large",
|
||||
}
|
||||
}
|
||||
s.datagramQueue.HandleDatagramFrame(f)
|
||||
return nil
|
||||
|
@ -1420,44 +1456,55 @@ func (s *session) shutdown() {
|
|||
<-s.ctx.Done()
|
||||
}
|
||||
|
||||
func (s *session) CloseWithError(code protocol.ApplicationErrorCode, desc string) error {
|
||||
s.closeLocal(qerr.NewApplicationError(qerr.ErrorCode(code), desc))
|
||||
func (s *session) CloseWithError(code ApplicationErrorCode, desc string) error {
|
||||
s.closeLocal(&qerr.ApplicationError{
|
||||
ErrorCode: code,
|
||||
ErrorMessage: desc,
|
||||
})
|
||||
<-s.ctx.Done()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *session) handleCloseError(closeErr closeError) {
|
||||
if closeErr.err == nil {
|
||||
closeErr.err = qerr.NewApplicationError(0, "")
|
||||
func (s *session) handleCloseError(closeErr *closeError) {
|
||||
e := closeErr.err
|
||||
if e == nil {
|
||||
e = &qerr.ApplicationError{}
|
||||
} else {
|
||||
defer func() {
|
||||
closeErr.err = e
|
||||
}()
|
||||
}
|
||||
|
||||
var quicErr *qerr.QuicError
|
||||
var ok bool
|
||||
if quicErr, ok = closeErr.err.(*qerr.QuicError); !ok {
|
||||
quicErr = qerr.ToQuicError(closeErr.err)
|
||||
var (
|
||||
statelessResetErr *StatelessResetError
|
||||
versionNegotiationErr *VersionNegotiationError
|
||||
recreateErr *errCloseForRecreating
|
||||
applicationErr *ApplicationError
|
||||
transportErr *TransportError
|
||||
)
|
||||
switch {
|
||||
case errors.Is(e, qerr.ErrIdleTimeout),
|
||||
errors.Is(e, qerr.ErrHandshakeTimeout),
|
||||
errors.As(e, &statelessResetErr),
|
||||
errors.As(e, &versionNegotiationErr),
|
||||
errors.As(e, &recreateErr),
|
||||
errors.As(e, &applicationErr),
|
||||
errors.As(e, &transportErr):
|
||||
default:
|
||||
e = &qerr.TransportError{
|
||||
ErrorCode: qerr.InternalError,
|
||||
ErrorMessage: e.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
s.streamsMap.CloseWithError(quicErr)
|
||||
s.streamsMap.CloseWithError(e)
|
||||
s.connIDManager.Close()
|
||||
if s.datagramQueue != nil {
|
||||
s.datagramQueue.CloseWithError(quicErr)
|
||||
s.datagramQueue.CloseWithError(e)
|
||||
}
|
||||
|
||||
if s.tracer != nil {
|
||||
// timeout errors are logged as soon as they occur (to distinguish between handshake and idle timeouts)
|
||||
if nerr, ok := closeErr.err.(net.Error); !ok || !nerr.Timeout() {
|
||||
var resetErr statelessResetErr
|
||||
var vnErr errVersionNegotiation
|
||||
if errors.As(closeErr.err, &resetErr) {
|
||||
s.tracer.ClosedConnection(logging.NewStatelessResetCloseReason(resetErr.token))
|
||||
} else if errors.As(closeErr.err, &vnErr) {
|
||||
s.tracer.ClosedConnection(logging.NewVersionNegotiationError(vnErr.theirVersions))
|
||||
} else if quicErr.IsApplicationError() {
|
||||
s.tracer.ClosedConnection(logging.NewApplicationCloseReason(quicErr.ErrorCode, closeErr.remote))
|
||||
} else {
|
||||
s.tracer.ClosedConnection(logging.NewTransportCloseReason(quicErr.ErrorCode, closeErr.remote))
|
||||
}
|
||||
}
|
||||
if s.tracer != nil && !errors.As(e, &recreateErr) {
|
||||
s.tracer.ClosedConnection(e)
|
||||
}
|
||||
|
||||
// If this is a remote close we're done here
|
||||
|
@ -1469,7 +1516,7 @@ func (s *session) handleCloseError(closeErr closeError) {
|
|||
s.connIDGenerator.RemoveAll()
|
||||
return
|
||||
}
|
||||
connClosePacket, err := s.sendConnectionClose(quicErr)
|
||||
connClosePacket, err := s.sendConnectionClose(e)
|
||||
if err != nil {
|
||||
s.logger.Debugf("Error sending CONNECTION_CLOSE: %s", err)
|
||||
}
|
||||
|
@ -1508,7 +1555,10 @@ func (s *session) restoreTransportParameters(params *wire.TransportParameters) {
|
|||
|
||||
func (s *session) handleTransportParameters(params *wire.TransportParameters) {
|
||||
if err := s.checkTransportParameters(params); err != nil {
|
||||
s.closeLocal(err)
|
||||
s.closeLocal(&qerr.TransportError{
|
||||
ErrorCode: qerr.TransportParameterError,
|
||||
ErrorMessage: err.Error(),
|
||||
})
|
||||
}
|
||||
s.peerParams = params
|
||||
// On the client side we have to wait for handshake completion.
|
||||
|
@ -1531,7 +1581,7 @@ func (s *session) checkTransportParameters(params *wire.TransportParameters) err
|
|||
|
||||
// check the initial_source_connection_id
|
||||
if !params.InitialSourceConnectionID.Equal(s.handshakeDestConnID) {
|
||||
return qerr.NewError(qerr.TransportParameterError, fmt.Sprintf("expected initial_source_connection_id to equal %s, is %s", s.handshakeDestConnID, params.InitialSourceConnectionID))
|
||||
return fmt.Errorf("expected initial_source_connection_id to equal %s, is %s", s.handshakeDestConnID, params.InitialSourceConnectionID)
|
||||
}
|
||||
|
||||
if s.perspective == protocol.PerspectiveServer {
|
||||
|
@ -1539,17 +1589,17 @@ func (s *session) checkTransportParameters(params *wire.TransportParameters) err
|
|||
}
|
||||
// check the original_destination_connection_id
|
||||
if !params.OriginalDestinationConnectionID.Equal(s.origDestConnID) {
|
||||
return qerr.NewError(qerr.TransportParameterError, fmt.Sprintf("expected original_destination_connection_id to equal %s, is %s", s.origDestConnID, params.OriginalDestinationConnectionID))
|
||||
return fmt.Errorf("expected original_destination_connection_id to equal %s, is %s", s.origDestConnID, params.OriginalDestinationConnectionID)
|
||||
}
|
||||
if s.retrySrcConnID != nil { // a Retry was performed
|
||||
if params.RetrySourceConnectionID == nil {
|
||||
return qerr.NewError(qerr.TransportParameterError, "missing retry_source_connection_id")
|
||||
return errors.New("missing retry_source_connection_id")
|
||||
}
|
||||
if !(*params.RetrySourceConnectionID).Equal(*s.retrySrcConnID) {
|
||||
return qerr.NewError(qerr.TransportParameterError, fmt.Sprintf("expected retry_source_connection_id to equal %s, is %s", s.retrySrcConnID, *params.RetrySourceConnectionID))
|
||||
return fmt.Errorf("expected retry_source_connection_id to equal %s, is %s", s.retrySrcConnID, *params.RetrySourceConnectionID)
|
||||
}
|
||||
} else if params.RetrySourceConnectionID != nil {
|
||||
return qerr.NewError(qerr.TransportParameterError, "received retry_source_connection_id, although no Retry was performed")
|
||||
return errors.New("received retry_source_connection_id, although no Retry was performed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -1718,7 +1768,7 @@ func (s *session) sendPacket() (bool, error) {
|
|||
s.sendQueue.Send(packet.buffer)
|
||||
return true, nil
|
||||
}
|
||||
if !s.config.DisablePathMTUDiscovery && s.handshakeComplete && s.mtuDiscoverer.ShouldSendProbe(now) {
|
||||
if !s.config.DisablePathMTUDiscovery && s.mtuDiscoverer.ShouldSendProbe(now) {
|
||||
packet, err := s.packer.PackMTUProbePacket(s.mtuDiscoverer.GetPing())
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
@ -1744,8 +1794,21 @@ func (s *session) sendPackedPacket(packet *packedPacket, now time.Time) {
|
|||
s.sendQueue.Send(packet.buffer)
|
||||
}
|
||||
|
||||
func (s *session) sendConnectionClose(quicErr *qerr.QuicError) ([]byte, error) {
|
||||
packet, err := s.packer.PackConnectionClose(quicErr)
|
||||
func (s *session) sendConnectionClose(e error) ([]byte, error) {
|
||||
var packet *coalescedPacket
|
||||
var err error
|
||||
var transportErr *qerr.TransportError
|
||||
var applicationErr *qerr.ApplicationError
|
||||
if errors.As(e, &transportErr) {
|
||||
packet, err = s.packer.PackConnectionClose(transportErr)
|
||||
} else if errors.As(e, &applicationErr) {
|
||||
packet, err = s.packer.PackApplicationClose(applicationErr)
|
||||
} else {
|
||||
packet, err = s.packer.PackConnectionClose(&qerr.TransportError{
|
||||
ErrorCode: qerr.InternalError,
|
||||
ErrorMessage: fmt.Sprintf("session BUG: unspecified error type (msg: %s)", e.Error()),
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1902,8 +1965,7 @@ func (s *session) SendMessage(p []byte) error {
|
|||
}
|
||||
f.Data = make([]byte, len(p))
|
||||
copy(f.Data, p)
|
||||
s.datagramQueue.AddAndWait(f)
|
||||
return nil
|
||||
return s.datagramQueue.AddAndWait(f)
|
||||
}
|
||||
|
||||
func (s *session) ReceiveMessage() ([]byte, error) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue