sync: quic-go 0.42.0

Signed-off-by: Gaukas Wang <i@gaukas.wang>
This commit is contained in:
Gaukas Wang 2024-04-23 22:34:55 -06:00
parent d40dde9b9b
commit 4973374ea5
No known key found for this signature in database
GPG key ID: 6F0DF52D710D8189
252 changed files with 13121 additions and 5437 deletions

View file

@ -1,8 +1,23 @@
module test
go 1.16
go 1.21
// The version doesn't matter here, as we're replacing it with the currently checked out code anyway.
require github.com/refraction-networking/uquic v0.21.0
require github.com/quic-go/quic-go v0.21.0
replace github.com/refraction-networking/uquic => ../../
require (
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
go.uber.org/mock v0.4.0 // indirect
golang.org/x/crypto v0.4.0 // indirect
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
golang.org/x/mod v0.11.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/tools v0.9.1 // indirect
)
replace github.com/quic-go/quic-go => ../../

View file

@ -1,364 +1,53 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
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=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
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-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
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/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=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
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/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/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/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-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
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/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
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.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU=
github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0=
github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo=
github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw=
github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo=
github.com/onsi/ginkgo/v2 v2.8.1/go.mod h1:N1/NbDngAFcSLdyZ+/aYTYGSlq9qMCS/cNKGJjy+csc=
github.com/onsi/ginkgo/v2 v2.9.0/go.mod h1:4xkjoL/tZv4SMWeww56BU5kAt19mVB47gTWxmrTcxyk=
github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo=
github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo=
github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc=
github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM=
github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg=
github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM=
github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
github.com/onsi/gomega v1.27.1/go.mod h1:aHX5xOykVYzWOV4WqQy0sy8BQptgukenXpCXfadcIAw=
github.com/onsi/gomega v1.27.3/go.mod h1:5vG284IBtfDAmDyrK+eGyZmUgUlmi+Wngqo557cZ6Gw=
github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ=
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
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=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg=
github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
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/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/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
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.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
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=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
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.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
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=
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
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=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.2.0/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=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/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-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
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=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
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.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
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=
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=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
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/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/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.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/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
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=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=

View file

@ -31,7 +31,7 @@ var _ = Describe("Stream Cancellations", func() {
server, err = quic.ListenAddr("localhost:0", getTLSConfig(), getQuicConfig(nil))
Expect(err).ToNot(HaveOccurred())
var canceledCounter int32
var canceledCounter atomic.Int32
go func() {
defer GinkgoRecover()
var wg sync.WaitGroup
@ -50,18 +50,18 @@ var _ = Describe("Stream Cancellations", func() {
ErrorCode: quic.StreamErrorCode(str.StreamID()),
Remote: true,
}))
atomic.AddInt32(&canceledCounter, 1)
canceledCounter.Add(1)
return
}
if err := str.Close(); err != nil {
Expect(err).To(MatchError(fmt.Sprintf("close called for canceled stream %d", str.StreamID())))
atomic.AddInt32(&canceledCounter, 1)
canceledCounter.Add(1)
return
}
}()
}
wg.Wait()
numCanceledStreamsChan <- atomic.LoadInt32(&canceledCounter)
numCanceledStreamsChan <- canceledCounter.Load()
}()
return numCanceledStreamsChan
}
@ -80,7 +80,7 @@ var _ = Describe("Stream Cancellations", func() {
)
Expect(err).ToNot(HaveOccurred())
var canceledCounter int32
var canceledCounter atomic.Int32
var wg sync.WaitGroup
wg.Add(numStreams)
for i := 0; i < numStreams; i++ {
@ -91,7 +91,7 @@ var _ = Describe("Stream Cancellations", func() {
Expect(err).ToNot(HaveOccurred())
// cancel around 2/3 of the streams
if rand.Int31()%3 != 0 {
atomic.AddInt32(&canceledCounter, 1)
canceledCounter.Add(1)
resetErr := quic.StreamErrorCode(str.StreamID())
str.CancelRead(resetErr)
_, err := str.Read([]byte{0})
@ -113,7 +113,7 @@ var _ = Describe("Stream Cancellations", func() {
Eventually(serverCanceledCounterChan).Should(Receive(&serverCanceledCounter))
Expect(conn.CloseWithError(0, "")).To(Succeed())
clientCanceledCounter := atomic.LoadInt32(&canceledCounter)
clientCanceledCounter := canceledCounter.Load()
// The server will only count a stream as being reset if learns about the cancelation before it finished writing all data.
Expect(clientCanceledCounter).To(BeNumerically(">=", serverCanceledCounter))
fmt.Fprintf(GinkgoWriter, "Canceled reading on %d of %d streams.\n", clientCanceledCounter, numStreams)
@ -132,7 +132,7 @@ var _ = Describe("Stream Cancellations", func() {
)
Expect(err).ToNot(HaveOccurred())
var canceledCounter int32
var canceledCounter atomic.Int32
var wg sync.WaitGroup
wg.Add(numStreams)
for i := 0; i < numStreams; i++ {
@ -148,7 +148,7 @@ var _ = Describe("Stream Cancellations", func() {
Expect(err).ToNot(HaveOccurred())
str.CancelRead(quic.StreamErrorCode(str.StreamID()))
Expect(data).To(Equal(PRData[:length]))
atomic.AddInt32(&canceledCounter, 1)
canceledCounter.Add(1)
return
}
data, err := io.ReadAll(str)
@ -162,7 +162,7 @@ var _ = Describe("Stream Cancellations", func() {
Eventually(serverCanceledCounterChan).Should(Receive(&serverCanceledCounter))
Expect(conn.CloseWithError(0, "")).To(Succeed())
clientCanceledCounter := atomic.LoadInt32(&canceledCounter)
clientCanceledCounter := canceledCounter.Load()
// The server will only count a stream as being reset if learns about the cancelation before it finished writing all data.
Expect(clientCanceledCounter).To(BeNumerically(">=", serverCanceledCounter))
fmt.Fprintf(GinkgoWriter, "Canceled reading on %d of %d streams.\n", clientCanceledCounter, numStreams)
@ -185,7 +185,7 @@ var _ = Describe("Stream Cancellations", func() {
var wg sync.WaitGroup
wg.Add(numStreams)
var counter int32
var counter atomic.Int32
for i := 0; i < numStreams; i++ {
go func() {
defer GinkgoRecover()
@ -199,7 +199,7 @@ var _ = Describe("Stream Cancellations", func() {
defer close(done)
b := make([]byte, 32)
if _, err := str.Read(b); err != nil {
atomic.AddInt32(&counter, 1)
counter.Add(1)
Expect(err).To(Equal(&quic.StreamError{
StreamID: str.StreamID(),
ErrorCode: 1234,
@ -214,7 +214,7 @@ var _ = Describe("Stream Cancellations", func() {
}
wg.Wait()
Expect(conn.CloseWithError(0, "")).To(Succeed())
numCanceled := atomic.LoadInt32(&counter)
numCanceled := counter.Load()
fmt.Fprintf(GinkgoWriter, "canceled %d out of %d streams", numCanceled, numStreams)
Expect(numCanceled).ToNot(BeZero())
Eventually(serverCanceledCounterChan).Should(Receive())
@ -232,7 +232,7 @@ var _ = Describe("Stream Cancellations", func() {
Expect(err).ToNot(HaveOccurred())
var wg sync.WaitGroup
var counter int32
var counter atomic.Int32
wg.Add(numStreams)
for i := 0; i < numStreams; i++ {
go func() {
@ -242,7 +242,7 @@ var _ = Describe("Stream Cancellations", func() {
Expect(err).ToNot(HaveOccurred())
data, err := io.ReadAll(str)
if err != nil {
atomic.AddInt32(&counter, 1)
counter.Add(1)
Expect(err).To(MatchError(&quic.StreamError{
StreamID: str.StreamID(),
ErrorCode: quic.StreamErrorCode(str.StreamID()),
@ -254,7 +254,7 @@ var _ = Describe("Stream Cancellations", func() {
}
wg.Wait()
streamCount := atomic.LoadInt32(&counter)
streamCount := counter.Load()
fmt.Fprintf(GinkgoWriter, "Canceled writing on %d of %d streams\n", streamCount, numStreams)
Expect(streamCount).To(BeNumerically(">", numStreams/10))
Expect(numStreams - streamCount).To(BeNumerically(">", numStreams/10))
@ -267,7 +267,7 @@ var _ = Describe("Stream Cancellations", func() {
server, err := quic.ListenAddr("localhost:0", getTLSConfig(), nil)
Expect(err).ToNot(HaveOccurred())
var canceledCounter int32
var canceledCounter atomic.Int32
go func() {
defer GinkgoRecover()
conn, err := server.Accept(context.Background())
@ -280,7 +280,7 @@ var _ = Describe("Stream Cancellations", func() {
// cancel about 2/3 of the streams
if rand.Int31()%3 != 0 {
str.CancelWrite(quic.StreamErrorCode(str.StreamID()))
atomic.AddInt32(&canceledCounter, 1)
canceledCounter.Add(1)
return
}
_, err = str.Write(PRData)
@ -291,14 +291,14 @@ var _ = Describe("Stream Cancellations", func() {
}()
clientCanceledStreams := runClient(server)
Expect(clientCanceledStreams).To(Equal(atomic.LoadInt32(&canceledCounter)))
Expect(clientCanceledStreams).To(Equal(canceledCounter.Load()))
})
It("downloads when the server cancels some streams after sending some data", func() {
server, err := quic.ListenAddr("localhost:0", getTLSConfig(), nil)
Expect(err).ToNot(HaveOccurred())
var canceledCounter int32
var canceledCounter atomic.Int32
go func() {
defer GinkgoRecover()
conn, err := server.Accept(context.Background())
@ -314,7 +314,7 @@ var _ = Describe("Stream Cancellations", func() {
_, err = str.Write(PRData[:length])
Expect(err).ToNot(HaveOccurred())
str.CancelWrite(quic.StreamErrorCode(str.StreamID()))
atomic.AddInt32(&canceledCounter, 1)
canceledCounter.Add(1)
return
}
_, err = str.Write(PRData)
@ -325,7 +325,7 @@ var _ = Describe("Stream Cancellations", func() {
}()
clientCanceledStreams := runClient(server)
Expect(clientCanceledStreams).To(Equal(atomic.LoadInt32(&canceledCounter)))
Expect(clientCanceledStreams).To(Equal(canceledCounter.Load()))
})
})
@ -378,7 +378,7 @@ var _ = Describe("Stream Cancellations", func() {
Expect(err).ToNot(HaveOccurred())
var wg sync.WaitGroup
var counter int32
var counter atomic.Int32
wg.Add(numStreams)
for i := 0; i < numStreams; i++ {
go func() {
@ -399,13 +399,13 @@ var _ = Describe("Stream Cancellations", func() {
}))
return
}
atomic.AddInt32(&counter, 1)
counter.Add(1)
Expect(data).To(Equal(PRData))
}()
}
wg.Wait()
count := atomic.LoadInt32(&counter)
count := counter.Load()
Expect(count).To(BeNumerically(">", numStreams/15))
fmt.Fprintf(GinkgoWriter, "Successfully read from %d of %d streams.\n", count, numStreams)
@ -464,7 +464,7 @@ var _ = Describe("Stream Cancellations", func() {
Expect(err).ToNot(HaveOccurred())
var wg sync.WaitGroup
var counter int32
var counter atomic.Int32
wg.Add(numStreams)
for i := 0; i < numStreams; i++ {
go func() {
@ -495,14 +495,14 @@ var _ = Describe("Stream Cancellations", func() {
return
}
atomic.AddInt32(&counter, 1)
counter.Add(1)
Expect(data).To(Equal(PRData))
}()
}
wg.Wait()
Eventually(done).Should(BeClosed())
count := atomic.LoadInt32(&counter)
count := counter.Load()
Expect(count).To(BeNumerically(">", numStreams/15))
fmt.Fprintf(GinkgoWriter, "Successfully read from %d of %d streams.\n", count, numStreams)
@ -543,7 +543,7 @@ var _ = Describe("Stream Cancellations", func() {
Expect(err).ToNot(HaveOccurred())
var numToAccept int
var counter int32
var counter atomic.Int32
var wg sync.WaitGroup
wg.Add(numStreams)
for numToAccept < numStreams {
@ -561,7 +561,7 @@ var _ = Describe("Stream Cancellations", func() {
str, err := conn.AcceptUniStream(ctx)
if err != nil {
if err.Error() == "context canceled" {
atomic.AddInt32(&counter, 1)
counter.Add(1)
}
return
}
@ -573,7 +573,7 @@ var _ = Describe("Stream Cancellations", func() {
}
wg.Wait()
count := atomic.LoadInt32(&counter)
count := counter.Load()
fmt.Fprintf(GinkgoWriter, "Canceled AcceptStream %d times\n", count)
Expect(count).To(BeNumerically(">", numStreams/2))
Expect(conn.CloseWithError(0, "")).To(Succeed())
@ -589,7 +589,7 @@ var _ = Describe("Stream Cancellations", func() {
Expect(err).ToNot(HaveOccurred())
msg := make(chan struct{}, 1)
var numCanceled int32
var numCanceled atomic.Int32
go func() {
defer GinkgoRecover()
defer close(msg)
@ -603,7 +603,7 @@ var _ = Describe("Stream Cancellations", func() {
str, err := conn.OpenUniStreamSync(ctx)
if err != nil {
Expect(err).To(MatchError(context.DeadlineExceeded))
atomic.AddInt32(&numCanceled, 1)
numCanceled.Add(1)
select {
case msg <- struct{}{}:
default:
@ -644,7 +644,7 @@ var _ = Describe("Stream Cancellations", func() {
}
wg.Wait()
count := atomic.LoadInt32(&numCanceled)
count := numCanceled.Load()
fmt.Fprintf(GinkgoWriter, "Canceled OpenStreamSync %d times\n", count)
Expect(count).To(BeNumerically(">=", numStreams-maxIncomingStreams))
Expect(conn.CloseWithError(0, "")).To(Succeed())

View file

@ -50,6 +50,7 @@ var _ = Describe("Connection ID lengths tests", func() {
ConnectionIDLength: connIDLen,
ConnectionIDGenerator: connIDGenerator,
}
addTracer(tr)
ln, err := tr.Listen(getTLSConfig(), getQuicConfig(nil))
Expect(err).ToNot(HaveOccurred())
go func() {
@ -92,6 +93,7 @@ var _ = Describe("Connection ID lengths tests", func() {
ConnectionIDLength: connIDLen,
ConnectionIDGenerator: connIDGenerator,
}
addTracer(tr)
defer tr.Close()
cl, err := tr.Dial(
context.Background(),

View file

@ -19,11 +19,12 @@ import (
)
var _ = Describe("Datagram test", func() {
const num = 100
const concurrentSends = 100
const maxDatagramSize = 250
var (
serverConn, clientConn *net.UDPConn
dropped, total int32
dropped, total atomic.Int32
)
startServerAndProxy := func(enableDatagram, expectDatagramSupport bool) (port int, closeFn func()) {
@ -47,19 +48,24 @@ var _ = Describe("Datagram test", func() {
if expectDatagramSupport {
Expect(conn.ConnectionState().SupportsDatagrams).To(BeTrue())
if enableDatagram {
f := &wire.DatagramFrame{DataLenPresent: true}
var wg sync.WaitGroup
wg.Add(num)
for i := 0; i < num; i++ {
wg.Add(concurrentSends)
for i := 0; i < concurrentSends; i++ {
go func(i int) {
defer GinkgoRecover()
defer wg.Done()
b := make([]byte, 8)
binary.BigEndian.PutUint64(b, uint64(i))
Expect(conn.SendMessage(b)).To(Succeed())
Expect(conn.SendDatagram(b)).To(Succeed())
}(i)
}
maxDatagramMessageSize := f.MaxDataLen(maxDatagramSize, conn.ConnectionState().Version)
b := make([]byte, maxDatagramMessageSize+1)
Expect(conn.SendDatagram(b)).To(MatchError(&quic.DatagramTooLargeError{
PeerMaxDatagramFrameSize: int64(maxDatagramMessageSize),
}))
wg.Wait()
}
} else {
@ -81,9 +87,9 @@ var _ = Describe("Datagram test", func() {
}
drop := mrand.Int()%10 == 0
if drop {
atomic.AddInt32(&dropped, 1)
dropped.Add(1)
}
atomic.AddInt32(&total, 1)
total.Add(1)
return drop
},
})
@ -103,6 +109,8 @@ var _ = Describe("Datagram test", func() {
})
It("sends datagrams", func() {
oldMaxDatagramSize := wire.MaxDatagramSize
wire.MaxDatagramSize = maxDatagramSize
proxyPort, close := startServerAndProxy(true, true)
defer close()
raddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", proxyPort))
@ -120,22 +128,23 @@ var _ = Describe("Datagram test", func() {
for {
// Close the connection if no message is received for 100 ms.
timer := time.AfterFunc(scaleDuration(100*time.Millisecond), func() { conn.CloseWithError(0, "") })
if _, err := conn.ReceiveMessage(context.Background()); err != nil {
if _, err := conn.ReceiveDatagram(context.Background()); err != nil {
break
}
timer.Stop()
counter++
}
numDropped := int(atomic.LoadInt32(&dropped))
expVal := num - numDropped
fmt.Fprintf(GinkgoWriter, "Dropped %d out of %d packets.\n", numDropped, atomic.LoadInt32(&total))
fmt.Fprintf(GinkgoWriter, "Received %d out of %d sent datagrams.\n", counter, num)
numDropped := int(dropped.Load())
expVal := concurrentSends - numDropped
fmt.Fprintf(GinkgoWriter, "Dropped %d out of %d packets.\n", numDropped, total.Load())
fmt.Fprintf(GinkgoWriter, "Received %d out of %d sent datagrams.\n", counter, concurrentSends)
Expect(counter).To(And(
BeNumerically(">", expVal*9/10),
BeNumerically("<", num),
BeNumerically("<", concurrentSends),
))
Eventually(conn.Context().Done).Should(BeClosed())
wire.MaxDatagramSize = oldMaxDatagramSize
})
It("server can disable datagram", func() {
@ -170,7 +179,7 @@ var _ = Describe("Datagram test", func() {
Expect(err).ToNot(HaveOccurred())
Expect(conn.ConnectionState().SupportsDatagrams).To(BeFalse())
Expect(conn.SendMessage([]byte{0})).To(HaveOccurred())
Expect(conn.SendDatagram([]byte{0})).To(HaveOccurred())
close()
conn.CloseWithError(0, "")

View file

@ -13,7 +13,7 @@ import (
)
var _ = Describe("Stream deadline tests", func() {
setup := func() (*quic.Listener, quic.Stream, quic.Stream) {
setup := func() (serverStr, clientStr quic.Stream, close func()) {
server, err := quic.ListenAddr("localhost:0", getTLSConfig(), getQuicConfig(nil))
Expect(err).ToNot(HaveOccurred())
strChan := make(chan quic.SendStream)
@ -35,19 +35,21 @@ var _ = Describe("Stream deadline tests", func() {
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
clientStr, err := conn.OpenStream()
clientStr, err = conn.OpenStream()
Expect(err).ToNot(HaveOccurred())
_, err = clientStr.Write([]byte{0}) // need to write one byte so the server learns about the stream
Expect(err).ToNot(HaveOccurred())
var serverStr quic.Stream
Eventually(strChan).Should(Receive(&serverStr))
return server, serverStr, clientStr
return serverStr, clientStr, func() {
Expect(server.Close()).To(Succeed())
Expect(conn.CloseWithError(0, "")).To(Succeed())
}
}
Context("read deadlines", func() {
It("completes a transfer when the deadline is set", func() {
server, serverStr, clientStr := setup()
defer server.Close()
serverStr, clientStr, closeFn := setup()
defer closeFn()
const timeout = time.Millisecond
done := make(chan struct{})
@ -81,8 +83,8 @@ var _ = Describe("Stream deadline tests", func() {
})
It("completes a transfer when the deadline is set concurrently", func() {
server, serverStr, clientStr := setup()
defer server.Close()
serverStr, clientStr, closeFn := setup()
defer closeFn()
const timeout = time.Millisecond
go func() {
@ -131,8 +133,8 @@ var _ = Describe("Stream deadline tests", func() {
Context("write deadlines", func() {
It("completes a transfer when the deadline is set", func() {
server, serverStr, clientStr := setup()
defer server.Close()
serverStr, clientStr, closeFn := setup()
defer closeFn()
const timeout = time.Millisecond
done := make(chan struct{})
@ -164,8 +166,8 @@ var _ = Describe("Stream deadline tests", func() {
})
It("completes a transfer when the deadline is set concurrently", func() {
server, serverStr, clientStr := setup()
defer server.Close()
serverStr, clientStr, closeFn := setup()
defer closeFn()
const timeout = time.Millisecond
readDone := make(chan struct{})

View file

@ -67,14 +67,14 @@ var _ = Describe("Drop Tests", func() {
fmt.Fprintf(GinkgoWriter, "Dropping packets for %s, after a delay of %s\n", dropDuration, dropDelay)
startTime := time.Now()
var numDroppedPackets int32
var numDroppedPackets atomic.Int32
startListenerAndProxy(func(d quicproxy.Direction, _ []byte) bool {
if !d.Is(direction) {
return false
}
drop := time.Now().After(startTime.Add(dropDelay)) && time.Now().Before(startTime.Add(dropDelay).Add(dropDuration))
if drop {
atomic.AddInt32(&numDroppedPackets, 1)
numDroppedPackets.Add(1)
}
return drop
})
@ -114,7 +114,7 @@ var _ = Describe("Drop Tests", func() {
Expect(b[0]).To(Equal(i))
}
close(done)
numDropped := atomic.LoadInt32(&numDroppedPackets)
numDropped := numDroppedPackets.Load()
fmt.Fprintf(GinkgoWriter, "Dropped %d packets.\n", numDropped)
Expect(numDropped).To(BeNumerically(">", 0))
})

View file

@ -1,21 +0,0 @@
//go:build go1.19 && !go1.20
package self_test
import (
"errors"
"net/http"
"time"
)
const go120 = false
var errNotSupported = errors.New("not supported")
func setReadDeadline(w http.ResponseWriter, deadline time.Time) error {
return errNotSupported
}
func setWriteDeadline(w http.ResponseWriter, deadline time.Time) error {
return errNotSupported
}

View file

@ -1,22 +0,0 @@
//go:build go1.20
package self_test
import (
"net/http"
"time"
)
const go120 = true
func setReadDeadline(w http.ResponseWriter, deadline time.Time) error {
rc := http.NewResponseController(w)
return rc.SetReadDeadline(deadline)
}
func setWriteDeadline(w http.ResponseWriter, deadline time.Time) error {
rc := http.NewResponseController(w)
return rc.SetWriteDeadline(deadline)
}

View file

@ -10,13 +10,12 @@ import (
"sync/atomic"
"time"
quic "github.com/refraction-networking/uquic"
tls "github.com/refraction-networking/utls"
"github.com/refraction-networking/uquic/quicvarint"
quic "github.com/refraction-networking/uquic"
quicproxy "github.com/refraction-networking/uquic/integrationtests/tools/proxy"
"github.com/refraction-networking/uquic/internal/wire"
"github.com/refraction-networking/uquic/quicvarint"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
@ -27,23 +26,17 @@ var directions = []quicproxy.Direction{quicproxy.DirectionIncoming, quicproxy.Di
type applicationProtocol struct {
name string
run func()
run func(ln *quic.Listener, port int)
}
var _ = Describe("Handshake drop tests", func() {
var (
proxy *quicproxy.QuicProxy
ln *quic.Listener
)
data := GeneratePRData(5000)
const timeout = 2 * time.Minute
startListenerAndProxy := func(dropCallback quicproxy.DropCallback, doRetry bool, longCertChain bool) {
startListenerAndProxy := func(dropCallback quicproxy.DropCallback, doRetry bool, longCertChain bool) (ln *quic.Listener, proxyPort int, closeFn func()) {
conf := getQuicConfig(&quic.Config{
MaxIdleTimeout: timeout,
HandshakeIdleTimeout: timeout,
RequireAddressValidation: func(net.Addr) bool { return doRetry },
MaxIdleTimeout: timeout,
HandshakeIdleTimeout: timeout,
})
var tlsConf *tls.Config
if longCertChain {
@ -51,11 +44,18 @@ var _ = Describe("Handshake drop tests", func() {
} else {
tlsConf = getTLSConfig()
}
var err error
ln, err = quic.ListenAddr("localhost:0", tlsConf, conf)
laddr, err := net.ResolveUDPAddr("udp", "localhost:0")
Expect(err).ToNot(HaveOccurred())
conn, err := net.ListenUDP("udp", laddr)
Expect(err).ToNot(HaveOccurred())
tr := &quic.Transport{Conn: conn}
if doRetry {
tr.VerifySourceAddress = func(net.Addr) bool { return true }
}
ln, err = tr.Listen(tlsConf, conf)
Expect(err).ToNot(HaveOccurred())
serverPort := ln.Addr().(*net.UDPAddr).Port
proxy, err = quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
RemoteAddr: fmt.Sprintf("localhost:%d", serverPort),
DropPacket: dropCallback,
DelayPacket: func(dir quicproxy.Direction, packet []byte) time.Duration {
@ -63,11 +63,18 @@ var _ = Describe("Handshake drop tests", func() {
},
})
Expect(err).ToNot(HaveOccurred())
return ln, proxy.LocalPort(), func() {
ln.Close()
tr.Close()
conn.Close()
proxy.Close()
}
}
clientSpeaksFirst := &applicationProtocol{
name: "client speaks first",
run: func() {
run: func(ln *quic.Listener, port int) {
serverConnChan := make(chan quic.Connection)
go func() {
defer GinkgoRecover()
@ -83,7 +90,7 @@ var _ = Describe("Handshake drop tests", func() {
}()
conn, err := quic.DialAddr(
context.Background(),
fmt.Sprintf("localhost:%d", proxy.LocalPort()),
fmt.Sprintf("localhost:%d", port),
getTLSClientConfig(),
getQuicConfig(&quic.Config{
MaxIdleTimeout: timeout,
@ -106,7 +113,7 @@ var _ = Describe("Handshake drop tests", func() {
serverSpeaksFirst := &applicationProtocol{
name: "server speaks first",
run: func() {
run: func(ln *quic.Listener, port int) {
serverConnChan := make(chan quic.Connection)
go func() {
defer GinkgoRecover()
@ -121,7 +128,7 @@ var _ = Describe("Handshake drop tests", func() {
}()
conn, err := quic.DialAddr(
context.Background(),
fmt.Sprintf("localhost:%d", proxy.LocalPort()),
fmt.Sprintf("localhost:%d", port),
getTLSClientConfig(),
getQuicConfig(&quic.Config{
MaxIdleTimeout: timeout,
@ -144,7 +151,7 @@ var _ = Describe("Handshake drop tests", func() {
nobodySpeaks := &applicationProtocol{
name: "nobody speaks",
run: func() {
run: func(ln *quic.Listener, port int) {
serverConnChan := make(chan quic.Connection)
go func() {
defer GinkgoRecover()
@ -154,7 +161,7 @@ var _ = Describe("Handshake drop tests", func() {
}()
conn, err := quic.DialAddr(
context.Background(),
fmt.Sprintf("localhost:%d", proxy.LocalPort()),
fmt.Sprintf("localhost:%d", port),
getTLSClientConfig(),
getQuicConfig(&quic.Config{
MaxIdleTimeout: timeout,
@ -170,11 +177,6 @@ var _ = Describe("Handshake drop tests", func() {
},
}
AfterEach(func() {
Expect(ln.Close()).To(Succeed())
Expect(proxy.Close()).To(Succeed())
})
for _, d := range directions {
direction := d
@ -195,35 +197,37 @@ var _ = Describe("Handshake drop tests", func() {
Context(app.name, func() {
It(fmt.Sprintf("establishes a connection when the first packet is lost in %s direction", direction), func() {
var incoming, outgoing int32
startListenerAndProxy(func(d quicproxy.Direction, _ []byte) bool {
var incoming, outgoing atomic.Int32
ln, proxyPort, closeFn := startListenerAndProxy(func(d quicproxy.Direction, _ []byte) bool {
var p int32
//nolint:exhaustive
switch d {
case quicproxy.DirectionIncoming:
p = atomic.AddInt32(&incoming, 1)
p = incoming.Add(1)
case quicproxy.DirectionOutgoing:
p = atomic.AddInt32(&outgoing, 1)
p = outgoing.Add(1)
}
return p == 1 && d.Is(direction)
}, doRetry, longCertChain)
app.run()
defer closeFn()
app.run(ln, proxyPort)
})
It(fmt.Sprintf("establishes a connection when the second packet is lost in %s direction", direction), func() {
var incoming, outgoing int32
startListenerAndProxy(func(d quicproxy.Direction, _ []byte) bool {
var incoming, outgoing atomic.Int32
ln, proxyPort, closeFn := startListenerAndProxy(func(d quicproxy.Direction, _ []byte) bool {
var p int32
//nolint:exhaustive
switch d {
case quicproxy.DirectionIncoming:
p = atomic.AddInt32(&incoming, 1)
p = incoming.Add(1)
case quicproxy.DirectionOutgoing:
p = atomic.AddInt32(&outgoing, 1)
p = outgoing.Add(1)
}
return p == 2 && d.Is(direction)
}, doRetry, longCertChain)
app.run()
defer closeFn()
app.run(ln, proxyPort)
})
It(fmt.Sprintf("establishes a connection when 1/3 of the packets are lost in %s direction", direction), func() {
@ -231,7 +235,7 @@ var _ = Describe("Handshake drop tests", func() {
var mx sync.Mutex
var incoming, outgoing int
startListenerAndProxy(func(d quicproxy.Direction, _ []byte) bool {
ln, proxyPort, closeFn := startListenerAndProxy(func(d quicproxy.Direction, _ []byte) bool {
drop := mrand.Int63n(int64(3)) == 0
mx.Lock()
@ -261,7 +265,8 @@ var _ = Describe("Handshake drop tests", func() {
}
return drop
}, doRetry, longCertChain)
app.run()
defer closeFn()
app.run(ln, proxyPort)
})
})
}
@ -282,13 +287,14 @@ var _ = Describe("Handshake drop tests", func() {
uint64(27+31*(1000+mrand.Int63()/31)) % quicvarint.Max: b,
}
startListenerAndProxy(func(d quicproxy.Direction, _ []byte) bool {
ln, proxyPort, closeFn := startListenerAndProxy(func(d quicproxy.Direction, _ []byte) bool {
if d == quicproxy.DirectionOutgoing {
return false
}
return mrand.Intn(3) == 0
}, false, false)
clientSpeaksFirst.run()
defer closeFn()
clientSpeaksFirst.run(ln, proxyPort)
})
}
})

View file

@ -55,9 +55,19 @@ var _ = Describe("Handshake RTT tests", func() {
// 1 RTT for verifying the source address
// 1 RTT for the TLS handshake
It("is forward-secure after 2 RTTs", func() {
serverConfig.RequireAddressValidation = func(net.Addr) bool { return true }
ln, err := quic.ListenAddr("localhost:0", serverTLSConfig, serverConfig)
It("is forward-secure after 2 RTTs with Retry", func() {
laddr, err := net.ResolveUDPAddr("udp", "localhost:0")
Expect(err).ToNot(HaveOccurred())
udpConn, err := net.ListenUDP("udp", laddr)
Expect(err).ToNot(HaveOccurred())
defer udpConn.Close()
tr := &quic.Transport{
Conn: udpConn,
VerifySourceAddress: func(net.Addr) bool { return true },
}
addTracer(tr)
defer tr.Close()
ln, err := tr.Listen(serverTLSConfig, serverConfig)
Expect(err).ToNot(HaveOccurred())
defer ln.Close()
@ -67,7 +77,10 @@ var _ = Describe("Handshake RTT tests", func() {
context.Background(),
fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port),
getTLSClientConfig(),
getQuicConfig(nil),
getQuicConfig(&quic.Config{GetConfigForClient: func(info *quic.ClientHelloInfo) (*quic.Config, error) {
Expect(info.AddrVerified).To(BeTrue())
return nil, nil
}}),
)
Expect(err).ToNot(HaveOccurred())
defer conn.CloseWithError(0, "")
@ -85,7 +98,10 @@ var _ = Describe("Handshake RTT tests", func() {
context.Background(),
fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port),
getTLSClientConfig(),
getQuicConfig(nil),
getQuicConfig(&quic.Config{GetConfigForClient: func(info *quic.ClientHelloInfo) (*quic.Config, error) {
Expect(info.AddrVerified).To(BeFalse())
return nil, nil
}}),
)
Expect(err).ToNot(HaveOccurred())
defer conn.CloseWithError(0, "")

View file

@ -80,6 +80,26 @@ var _ = Describe("Handshake tests", func() {
}()
}
It("returns the context cancellation error on timeouts", func() {
ctx, cancel := context.WithTimeout(context.Background(), scaleDuration(20*time.Millisecond))
defer cancel()
errChan := make(chan error, 1)
go func() {
_, err := quic.DialAddr(
ctx,
"localhost:1234", // nobody is listening on this port, but we're going to cancel this dial anyway
getTLSClientConfig(),
getQuicConfig(nil),
)
errChan <- err
}()
var err error
Eventually(errChan).Should(Receive(&err))
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(context.DeadlineExceeded))
})
It("returns the cancellation reason when a dial is canceled", func() {
ctx, cancel := context.WithCancelCause(context.Background())
errChan := make(chan error, 1)
@ -150,13 +170,14 @@ var _ = Describe("Handshake tests", func() {
Context("Certificate validation", func() {
It("accepts the certificate", func() {
runServer(getTLSConfig())
_, err := quic.DialAddr(
conn, err := quic.DialAddr(
context.Background(),
fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
getTLSClientConfig(),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
conn.CloseWithError(0, "")
})
It("has the right local and remote address on the tls.Config.GetConfigForClient ClientHelloInfo.Conn", func() {
@ -185,6 +206,7 @@ var _ = Describe("Handshake tests", func() {
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
defer conn.CloseWithError(0, "")
Eventually(done).Should(BeClosed())
Expect(server.Addr()).To(Equal(local))
Expect(conn.LocalAddr().(*net.UDPAddr).Port).To(Equal(remote.(*net.UDPAddr).Port))
@ -194,13 +216,14 @@ var _ = Describe("Handshake tests", func() {
It("works with a long certificate chain", func() {
runServer(getTLSConfigWithLongCertChain())
_, err := quic.DialAddr(
conn, err := quic.DialAddr(
context.Background(),
fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
getTLSClientConfig(),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
conn.CloseWithError(0, "")
})
It("errors if the server name doesn't match", func() {
@ -276,7 +299,7 @@ var _ = Describe("Handshake tests", func() {
})
})
Context("rate limiting", func() {
Context("queuening and accepting connections", func() {
var (
server *quic.Listener
pconn net.PacketConn
@ -301,7 +324,10 @@ var _ = Describe("Handshake tests", func() {
Expect(err).ToNot(HaveOccurred())
pconn, err = net.ListenUDP("udp", laddr)
Expect(err).ToNot(HaveOccurred())
dialer = &quic.Transport{Conn: pconn, ConnectionIDLength: 4}
dialer = &quic.Transport{
Conn: pconn,
ConnectionIDLength: 4,
}
})
AfterEach(func() {
@ -318,8 +344,11 @@ var _ = Describe("Handshake tests", func() {
}
time.Sleep(25 * time.Millisecond) // wait a bit for the connection to be queued
_, err := dial()
Expect(err).To(HaveOccurred())
conn, err := dial()
Expect(err).ToNot(HaveOccurred())
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel()
_, err = conn.AcceptStream(ctx)
var transportErr *quic.TransportError
Expect(errors.As(err, &transportErr)).To(BeTrue())
Expect(transportErr.ErrorCode).To(Equal(quic.ConnectionRefused))
@ -328,18 +357,21 @@ var _ = Describe("Handshake tests", func() {
_, err = server.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())
// dial again, and expect that this dial succeeds
conn, err := dial()
conn2, err := dial()
Expect(err).ToNot(HaveOccurred())
defer conn.CloseWithError(0, "")
defer conn2.CloseWithError(0, "")
time.Sleep(25 * time.Millisecond) // wait a bit for the connection to be queued
_, err = dial()
Expect(err).To(HaveOccurred())
conn3, err := dial()
Expect(err).ToNot(HaveOccurred())
ctx, cancel = context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel()
_, err = conn3.AcceptStream(ctx)
Expect(errors.As(err, &transportErr)).To(BeTrue())
Expect(transportErr.ErrorCode).To(Equal(quic.ConnectionRefused))
})
It("removes closed connections from the accept queue", func() {
It("also returns closed connections from the accept queue", func() {
firstConn, err := dial()
Expect(err).ToNot(HaveOccurred())
@ -350,25 +382,79 @@ var _ = Describe("Handshake tests", func() {
}
time.Sleep(scaleDuration(20 * time.Millisecond)) // wait a bit for the connection to be queued
_, err = dial()
Expect(err).To(HaveOccurred())
conn, err := dial()
Expect(err).ToNot(HaveOccurred())
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel()
_, err = conn.AcceptStream(ctx)
var transportErr *quic.TransportError
Expect(errors.As(err, &transportErr)).To(BeTrue())
Expect(transportErr.ErrorCode).To(Equal(quic.ConnectionRefused))
// Now close the one of the connection that are waiting to be accepted.
// This should free one spot in the queue.
Expect(firstConn.CloseWithError(0, ""))
const appErrCode quic.ApplicationErrorCode = 12345
Expect(firstConn.CloseWithError(appErrCode, ""))
Eventually(firstConn.Context().Done()).Should(BeClosed())
time.Sleep(scaleDuration(200 * time.Millisecond))
// dial again, and expect that this dial succeeds
_, err = dial()
// dial again, and expect that this fails again
conn2, err := dial()
Expect(err).ToNot(HaveOccurred())
time.Sleep(scaleDuration(20 * time.Millisecond)) // wait a bit for the connection to be queued
ctx, cancel = context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel()
_, err = conn2.AcceptStream(ctx)
Expect(errors.As(err, &transportErr)).To(BeTrue())
Expect(transportErr.ErrorCode).To(Equal(quic.ConnectionRefused))
_, err = dial()
Expect(err).To(HaveOccurred())
// now accept all connections
var closedConn quic.Connection
for i := 0; i < protocol.MaxAcceptQueueSize; i++ {
conn, err := server.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())
if conn.Context().Err() != nil {
if closedConn != nil {
Fail("only expected a single closed connection")
}
closedConn = conn
}
}
Expect(closedConn).ToNot(BeNil()) // there should be exactly one closed connection
_, err = closedConn.AcceptStream(context.Background())
var appErr *quic.ApplicationError
Expect(errors.As(err, &appErr)).To(BeTrue())
Expect(appErr.ErrorCode).To(Equal(appErrCode))
})
It("closes handshaking connections when the server is closed", func() {
laddr, err := net.ResolveUDPAddr("udp", "localhost:0")
Expect(err).ToNot(HaveOccurred())
udpConn, err := net.ListenUDP("udp", laddr)
Expect(err).ToNot(HaveOccurred())
tr := &quic.Transport{Conn: udpConn}
addTracer(tr)
defer tr.Close()
tlsConf := &tls.Config{}
done := make(chan struct{})
tlsConf.GetConfigForClient = func(info *tls.ClientHelloInfo) (*tls.Config, error) {
<-done
return nil, errors.New("closed")
}
ln, err := tr.Listen(tlsConf, getQuicConfig(nil))
Expect(err).ToNot(HaveOccurred())
errChan := make(chan error, 1)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
go func() {
defer GinkgoRecover()
_, err := quic.DialAddr(ctx, ln.Addr().String(), getTLSClientConfig(), getQuicConfig(nil))
errChan <- err
}()
time.Sleep(scaleDuration(20 * time.Millisecond)) // wait a bit for the connection to be queued
Expect(ln.Close()).To(Succeed())
close(done)
err = <-errChan
var transportErr *quic.TransportError
Expect(errors.As(err, &transportErr)).To(BeTrue())
Expect(transportErr.ErrorCode).To(Equal(quic.ConnectionRefused))
})
@ -474,14 +560,25 @@ var _ = Describe("Handshake tests", func() {
It("rejects invalid Retry token with the INVALID_TOKEN error", func() {
const rtt = 10 * time.Millisecond
serverConfig.RequireAddressValidation = func(net.Addr) bool { return true }
// The validity period of the retry token is the handshake timeout,
// which is twice the handshake idle timeout.
// By setting the handshake timeout shorter than the RTT, the token will have expired by the time
// it reaches the server.
serverConfig.HandshakeIdleTimeout = rtt / 5
server, err := quic.ListenAddr("localhost:0", getTLSConfig(), serverConfig)
laddr, err := net.ResolveUDPAddr("udp", "localhost:0")
Expect(err).ToNot(HaveOccurred())
udpConn, err := net.ListenUDP("udp", laddr)
Expect(err).ToNot(HaveOccurred())
defer udpConn.Close()
tr := &quic.Transport{
Conn: udpConn,
VerifySourceAddress: func(net.Addr) bool { return true },
}
addTracer(tr)
defer tr.Close()
server, err := tr.Listen(getTLSConfig(), serverConfig)
Expect(err).ToNot(HaveOccurred())
defer server.Close()

View file

@ -20,7 +20,7 @@ import (
type listenerWrapper struct {
http3.QUICEarlyListener
listenerClosed bool
count int32
count atomic.Int32
}
func (ln *listenerWrapper) Close() error {
@ -29,14 +29,18 @@ func (ln *listenerWrapper) Close() error {
}
func (ln *listenerWrapper) Faker() *fakeClosingListener {
atomic.AddInt32(&ln.count, 1)
ln.count.Add(1)
ctx, cancel := context.WithCancel(context.Background())
return &fakeClosingListener{ln, 0, ctx, cancel}
return &fakeClosingListener{
listenerWrapper: ln,
ctx: ctx,
cancel: cancel,
}
}
type fakeClosingListener struct {
*listenerWrapper
closed int32
closed atomic.Bool
ctx context.Context
cancel context.CancelFunc
}
@ -47,9 +51,9 @@ func (ln *fakeClosingListener) Accept(ctx context.Context) (quic.EarlyConnection
}
func (ln *fakeClosingListener) Close() error {
if atomic.CompareAndSwapInt32(&ln.closed, 0, 1) {
if ln.closed.CompareAndSwap(false, true) {
ln.cancel()
if atomic.AddInt32(&ln.listenerWrapper.count, -1) == 0 {
if ln.listenerWrapper.count.Add(-1) == 0 {
ln.listenerWrapper.Close()
}
}
@ -145,8 +149,8 @@ var _ = Describe("HTTP3 Server hotswap test", func() {
// and only the fake listener should be closed
Expect(server1.Close()).NotTo(HaveOccurred())
Eventually(stoppedServing1).Should(BeClosed())
Expect(fake1.closed).To(Equal(int32(1)))
Expect(fake2.closed).To(Equal(int32(0)))
Expect(fake1.closed.Load()).To(BeTrue())
Expect(fake2.closed.Load()).To(BeFalse())
Expect(ln.listenerClosed).ToNot(BeTrue())
Expect(client.Transport.(*http3.RoundTripper).Close()).NotTo(HaveOccurred())
@ -161,7 +165,7 @@ var _ = Describe("HTTP3 Server hotswap test", func() {
// close the other server - both the fake and the actual listeners must close now
Expect(server2.Close()).NotTo(HaveOccurred())
Eventually(stoppedServing2).Should(BeClosed())
Expect(fake2.closed).To(Equal(int32(1)))
Expect(fake2.closed.Load()).To(BeTrue())
Expect(ln.listenerClosed).To(BeTrue())
})
})

View file

@ -140,6 +140,26 @@ var _ = Describe("HTTP tests", func() {
Expect(resp.Header.Get("Content-Length")).To(Equal(strconv.Itoa(len("foobar"))))
})
It("detects stream errors when server panics when writing response", func() {
respChan := make(chan struct{})
mux.HandleFunc("/writing_and_panicking", func(w http.ResponseWriter, r *http.Request) {
// no recover here as it will interfere with the handler
w.Write([]byte("foobar"))
w.(http.Flusher).Flush()
// wait for the client to receive the response
<-respChan
panic(http.ErrAbortHandler)
})
resp, err := client.Get(fmt.Sprintf("https://localhost:%d/writing_and_panicking", port))
close(respChan)
Expect(err).ToNot(HaveOccurred())
body, err := io.ReadAll(resp.Body)
Expect(err).To(HaveOccurred())
// the body will be a prefix of what's written
Expect(bytes.HasPrefix([]byte("foobar"), body)).To(BeTrue())
})
It("requests to different servers with the same udpconn", func() {
resp, err := client.Get(fmt.Sprintf("https://localhost:%d/remoteAddr", port))
Expect(err).ToNot(HaveOccurred())
@ -299,6 +319,21 @@ var _ = Describe("HTTP tests", func() {
Expect(string(body)).To(Equal("Hello, World!\n"))
})
It("handles context cancellations", func() {
mux.HandleFunc("/cancel", func(w http.ResponseWriter, r *http.Request) {
<-r.Context().Done()
})
ctx, cancel := context.WithCancel(context.Background())
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://localhost:%d/cancel", port), nil)
Expect(err).ToNot(HaveOccurred())
time.AfterFunc(50*time.Millisecond, cancel)
_, err = client.Do(req)
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(context.Canceled))
})
It("cancels requests", func() {
handlerCalled := make(chan struct{})
mux.HandleFunc("/cancel", func(w http.ResponseWriter, r *http.Request) {
@ -432,55 +467,109 @@ var _ = Describe("HTTP tests", func() {
Eventually(done).Should(BeClosed())
})
if go120 {
It("supports read deadlines", func() {
mux.HandleFunc("/read-deadline", func(w http.ResponseWriter, r *http.Request) {
defer GinkgoRecover()
err := setReadDeadline(w, time.Now().Add(deadlineDelay))
Expect(err).ToNot(HaveOccurred())
It("supports read deadlines", func() {
mux.HandleFunc("/read-deadline", func(w http.ResponseWriter, r *http.Request) {
defer GinkgoRecover()
rc := http.NewResponseController(w)
Expect(rc.SetReadDeadline(time.Now().Add(deadlineDelay))).To(Succeed())
body, err := io.ReadAll(r.Body)
Expect(err).To(MatchError(os.ErrDeadlineExceeded))
Expect(body).To(ContainSubstring("aa"))
body, err := io.ReadAll(r.Body)
Expect(err).To(MatchError(os.ErrDeadlineExceeded))
Expect(body).To(ContainSubstring("aa"))
w.Write([]byte("ok"))
})
expectedEnd := time.Now().Add(deadlineDelay)
resp, err := client.Post(
fmt.Sprintf("https://localhost:%d/read-deadline", port),
"text/plain",
neverEnding('a'),
)
Expect(err).ToNot(HaveOccurred())
Expect(resp.StatusCode).To(Equal(200))
body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 2*deadlineDelay))
Expect(err).ToNot(HaveOccurred())
Expect(time.Now().After(expectedEnd)).To(BeTrue())
Expect(string(body)).To(Equal("ok"))
w.Write([]byte("ok"))
})
It("supports write deadlines", func() {
mux.HandleFunc("/write-deadline", func(w http.ResponseWriter, r *http.Request) {
defer GinkgoRecover()
err := setWriteDeadline(w, time.Now().Add(deadlineDelay))
Expect(err).ToNot(HaveOccurred())
expectedEnd := time.Now().Add(deadlineDelay)
resp, err := client.Post(
fmt.Sprintf("https://localhost:%d/read-deadline", port),
"text/plain",
neverEnding('a'),
)
Expect(err).ToNot(HaveOccurred())
Expect(resp.StatusCode).To(Equal(200))
_, err = io.Copy(w, neverEnding('a'))
Expect(err).To(MatchError(os.ErrDeadlineExceeded))
})
body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 2*deadlineDelay))
Expect(err).ToNot(HaveOccurred())
Expect(time.Now().After(expectedEnd)).To(BeTrue())
Expect(string(body)).To(Equal("ok"))
})
expectedEnd := time.Now().Add(deadlineDelay)
It("supports write deadlines", func() {
mux.HandleFunc("/write-deadline", func(w http.ResponseWriter, r *http.Request) {
defer GinkgoRecover()
rc := http.NewResponseController(w)
Expect(rc.SetWriteDeadline(time.Now().Add(deadlineDelay))).To(Succeed())
resp, err := client.Get(fmt.Sprintf("https://localhost:%d/write-deadline", port))
Expect(err).ToNot(HaveOccurred())
Expect(resp.StatusCode).To(Equal(200))
body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 2*deadlineDelay))
Expect(err).ToNot(HaveOccurred())
Expect(time.Now().After(expectedEnd)).To(BeTrue())
Expect(string(body)).To(ContainSubstring("aa"))
_, err := io.Copy(w, neverEnding('a'))
Expect(err).To(MatchError(os.ErrDeadlineExceeded))
})
}
expectedEnd := time.Now().Add(deadlineDelay)
resp, err := client.Get(fmt.Sprintf("https://localhost:%d/write-deadline", port))
Expect(err).ToNot(HaveOccurred())
Expect(resp.StatusCode).To(Equal(200))
body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 2*deadlineDelay))
Expect(err).ToNot(HaveOccurred())
Expect(time.Now().After(expectedEnd)).To(BeTrue())
Expect(string(body)).To(ContainSubstring("aa"))
})
It("sets remote address", func() {
mux.HandleFunc("/remote-addr", func(w http.ResponseWriter, r *http.Request) {
defer GinkgoRecover()
_, ok := r.Context().Value(http3.RemoteAddrContextKey).(net.Addr)
Expect(ok).To(BeTrue())
})
resp, err := client.Get(fmt.Sprintf("https://localhost:%d/remote-addr", port))
Expect(err).ToNot(HaveOccurred())
Expect(resp.StatusCode).To(Equal(200))
})
It("sets conn context", func() {
type ctxKey int
server.ConnContext = func(ctx context.Context, c quic.Connection) context.Context {
serv, ok := ctx.Value(http3.ServerContextKey).(*http3.Server)
Expect(ok).To(BeTrue())
Expect(serv).To(Equal(server))
ctx = context.WithValue(ctx, ctxKey(0), "Hello")
ctx = context.WithValue(ctx, ctxKey(1), c)
return ctx
}
mux.HandleFunc("/conn-context", func(w http.ResponseWriter, r *http.Request) {
defer GinkgoRecover()
v, ok := r.Context().Value(ctxKey(0)).(string)
Expect(ok).To(BeTrue())
Expect(v).To(Equal("Hello"))
c, ok := r.Context().Value(ctxKey(1)).(quic.Connection)
Expect(ok).To(BeTrue())
Expect(c).ToNot(BeNil())
serv, ok := r.Context().Value(http3.ServerContextKey).(*http3.Server)
Expect(ok).To(BeTrue())
Expect(serv).To(Equal(server))
})
resp, err := client.Get(fmt.Sprintf("https://localhost:%d/conn-context", port))
Expect(err).ToNot(HaveOccurred())
Expect(resp.StatusCode).To(Equal(200))
})
It("checks the server's settings", func() {
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("https://localhost:%d/hello", port), nil)
Expect(err).ToNot(HaveOccurred())
testErr := errors.New("test error")
_, err = rt.RoundTripOpt(req, http3.RoundTripOpt{CheckSettings: func(settings http3.Settings) error {
Expect(settings.EnableExtendedConnect).To(BeTrue())
Expect(settings.EnableDatagram).To(BeFalse())
Expect(settings.Other).To(BeEmpty())
return testErr
}})
Expect(err).To(MatchError(err))
})
})

View file

@ -15,8 +15,8 @@ import (
quic "github.com/refraction-networking/uquic"
quicproxy "github.com/refraction-networking/uquic/integrationtests/tools/proxy"
"github.com/refraction-networking/uquic/internal/protocol"
"github.com/refraction-networking/uquic/internal/testutils"
"github.com/refraction-networking/uquic/internal/wire"
"github.com/refraction-networking/uquic/testutils"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
@ -32,7 +32,7 @@ var _ = Describe("MITM test", func() {
serverConfig *quic.Config
)
startServerAndProxy := func(delayCb quicproxy.DelayCallback, dropCb quicproxy.DropCallback) (proxyPort int, closeFn func()) {
startServerAndProxy := func(delayCb quicproxy.DelayCallback, dropCb quicproxy.DropCallback, forceAddressValidation bool) (proxyPort int, closeFn func()) {
addr, err := net.ResolveUDPAddr("udp", "localhost:0")
Expect(err).ToNot(HaveOccurred())
c, err := net.ListenUDP("udp", addr)
@ -41,6 +41,10 @@ var _ = Describe("MITM test", func() {
Conn: c,
ConnectionIDLength: connIDLen,
}
addTracer(serverTransport)
if forceAddressValidation {
serverTransport.VerifySourceAddress = func(net.Addr) bool { return true }
}
ln, err := serverTransport.Listen(getTLSConfig(), serverConfig)
Expect(err).ToNot(HaveOccurred())
done := make(chan struct{})
@ -83,6 +87,7 @@ var _ = Describe("MITM test", func() {
Conn: clientUDPConn,
ConnectionIDLength: connIDLen,
}
addTracer(clientTransport)
})
Context("unsuccessful attacks", func() {
@ -153,7 +158,7 @@ var _ = Describe("MITM test", func() {
}
runTest := func(delayCb quicproxy.DelayCallback) {
proxyPort, closeFn := startServerAndProxy(delayCb, nil)
proxyPort, closeFn := startServerAndProxy(delayCb, nil, false)
defer closeFn()
raddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", proxyPort))
Expect(err).ToNot(HaveOccurred())
@ -196,7 +201,7 @@ var _ = Describe("MITM test", func() {
})
runTest := func(dropCb quicproxy.DropCallback) {
proxyPort, closeFn := startServerAndProxy(nil, dropCb)
proxyPort, closeFn := startServerAndProxy(nil, dropCb, false)
defer closeFn()
raddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", proxyPort))
Expect(err).ToNot(HaveOccurred())
@ -244,17 +249,17 @@ var _ = Describe("MITM test", func() {
Context("corrupting packets", func() {
const idleTimeout = time.Second
var numCorrupted, numPackets int32
var numCorrupted, numPackets atomic.Int32
BeforeEach(func() {
numCorrupted = 0
numPackets = 0
numCorrupted.Store(0)
numPackets.Store(0)
serverConfig.MaxIdleTimeout = idleTimeout
})
AfterEach(func() {
num := atomic.LoadInt32(&numCorrupted)
fmt.Fprintf(GinkgoWriter, "Corrupted %d of %d packets.", num, atomic.LoadInt32(&numPackets))
num := numCorrupted.Load()
fmt.Fprintf(GinkgoWriter, "Corrupted %d of %d packets.", num, numPackets.Load())
Expect(num).To(BeNumerically(">=", 1))
// If the packet containing the CONNECTION_CLOSE is corrupted,
// we have to wait for the connection to time out.
@ -266,13 +271,13 @@ var _ = Describe("MITM test", func() {
dropCb := func(dir quicproxy.Direction, raw []byte) bool {
defer GinkgoRecover()
if dir == quicproxy.DirectionIncoming {
atomic.AddInt32(&numPackets, 1)
numPackets.Add(1)
if rand.Intn(interval) == 0 {
pos := rand.Intn(len(raw))
raw[pos] = byte(rand.Intn(256))
_, err := clientTransport.WriteTo(raw, serverTransport.Conn.LocalAddr())
Expect(err).ToNot(HaveOccurred())
atomic.AddInt32(&numCorrupted, 1)
numCorrupted.Add(1)
return true
}
}
@ -286,13 +291,13 @@ var _ = Describe("MITM test", func() {
dropCb := func(dir quicproxy.Direction, raw []byte) bool {
defer GinkgoRecover()
if dir == quicproxy.DirectionOutgoing {
atomic.AddInt32(&numPackets, 1)
numPackets.Add(1)
if rand.Intn(interval) == 0 {
pos := rand.Intn(len(raw))
raw[pos] = byte(rand.Intn(256))
_, err := serverTransport.WriteTo(raw, clientTransport.Conn.LocalAddr())
Expect(err).ToNot(HaveOccurred())
atomic.AddInt32(&numCorrupted, 1)
numCorrupted.Add(1)
return true
}
}
@ -310,17 +315,16 @@ var _ = Describe("MITM test", func() {
const rtt = 20 * time.Millisecond
runTest := func(delayCb quicproxy.DelayCallback) (closeFn func(), err error) {
proxyPort, serverCloseFn := startServerAndProxy(delayCb, nil)
runTest := func(proxyPort int) (closeFn func(), err error) {
raddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", proxyPort))
Expect(err).ToNot(HaveOccurred())
_, err = clientTransport.Dial(
context.Background(),
raddr,
getTLSClientConfig(),
getQuicConfig(&quic.Config{HandshakeIdleTimeout: 2 * time.Second}),
getQuicConfig(&quic.Config{HandshakeIdleTimeout: scaleDuration(200 * time.Millisecond)}),
)
return func() { clientTransport.Close(); serverCloseFn() }, err
return func() { clientTransport.Close() }, err
}
// fails immediately because client connection closes when it can't find compatible version
@ -338,7 +342,7 @@ var _ = Describe("MITM test", func() {
}
// Create fake version negotiation packet with no supported versions
versions := []protocol.VersionNumber{}
versions := []protocol.Version{}
packet := wire.ComposeVersionNegotiation(
protocol.ArbitraryLenConnectionID(hdr.SrcConnectionID.Bytes()),
protocol.ArbitraryLenConnectionID(hdr.DestConnectionID.Bytes()),
@ -352,7 +356,9 @@ var _ = Describe("MITM test", func() {
}
return rtt / 2
}
closeFn, err := runTest(delayCb)
proxyPort, serverCloseFn := startServerAndProxy(delayCb, nil, false)
defer serverCloseFn()
closeFn, err := runTest(proxyPort)
defer closeFn()
Expect(err).To(HaveOccurred())
vnErr := &quic.VersionNegotiationError{}
@ -363,8 +369,7 @@ var _ = Describe("MITM test", func() {
// times out, because client doesn't accept subsequent real retry packets from server
// as it has already accepted a retry.
// TODO: determine behavior when server does not send Retry packets
It("fails when a forged Retry packet with modified srcConnID is sent to client", func() {
serverConfig.RequireAddressValidation = func(net.Addr) bool { return true }
It("fails when a forged Retry packet with modified Source Connection ID is sent to client", func() {
var initialPacketIntercepted bool
done := make(chan struct{})
delayCb := func(dir quicproxy.Direction, raw []byte) time.Duration {
@ -388,7 +393,9 @@ var _ = Describe("MITM test", func() {
}
return rtt / 2
}
closeFn, err := runTest(delayCb)
proxyPort, serverCloseFn := startServerAndProxy(delayCb, nil, true)
defer serverCloseFn()
closeFn, err := runTest(proxyPort)
defer closeFn()
Expect(err).To(HaveOccurred())
Expect(err.(net.Error).Timeout()).To(BeTrue())
@ -412,13 +419,15 @@ var _ = Describe("MITM test", func() {
}
defer close(done)
injected = true
initialPacket := testutils.ComposeInitialPacket(hdr.DestConnectionID, hdr.SrcConnectionID, hdr.Version, hdr.DestConnectionID, nil)
initialPacket := testutils.ComposeInitialPacket(hdr.DestConnectionID, hdr.SrcConnectionID, hdr.DestConnectionID, nil, protocol.PerspectiveServer, hdr.Version)
_, err = serverTransport.WriteTo(initialPacket, clientTransport.Conn.LocalAddr())
Expect(err).ToNot(HaveOccurred())
}
return rtt
}
closeFn, err := runTest(delayCb)
proxyPort, serverCloseFn := startServerAndProxy(delayCb, nil, false)
defer serverCloseFn()
closeFn, err := runTest(proxyPort)
defer closeFn()
Expect(err).To(HaveOccurred())
Expect(err.(net.Error).Timeout()).To(BeTrue())
@ -442,13 +451,15 @@ var _ = Describe("MITM test", func() {
injected = true
// Fake Initial with ACK for packet 2 (unsent)
ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 2, Largest: 2}}}
initialPacket := testutils.ComposeInitialPacket(hdr.DestConnectionID, hdr.SrcConnectionID, hdr.Version, hdr.DestConnectionID, []wire.Frame{ack})
initialPacket := testutils.ComposeInitialPacket(hdr.DestConnectionID, hdr.SrcConnectionID, hdr.DestConnectionID, []wire.Frame{ack}, protocol.PerspectiveServer, hdr.Version)
_, err = serverTransport.WriteTo(initialPacket, clientTransport.Conn.LocalAddr())
Expect(err).ToNot(HaveOccurred())
}
return rtt
}
closeFn, err := runTest(delayCb)
proxyPort, serverCloseFn := startServerAndProxy(delayCb, nil, false)
defer serverCloseFn()
closeFn, err := runTest(proxyPort)
defer closeFn()
Expect(err).To(HaveOccurred())
var transportErr *quic.TransportError

View file

@ -73,6 +73,7 @@ var _ = Describe("Multiplexing", func() {
Expect(err).ToNot(HaveOccurred())
defer conn.Close()
tr := &quic.Transport{Conn: conn}
addTracer(tr)
done1 := make(chan struct{})
done2 := make(chan struct{})
@ -108,6 +109,7 @@ var _ = Describe("Multiplexing", func() {
Expect(err).ToNot(HaveOccurred())
defer conn.Close()
tr := &quic.Transport{Conn: conn}
addTracer(tr)
done1 := make(chan struct{})
done2 := make(chan struct{})
@ -138,6 +140,7 @@ var _ = Describe("Multiplexing", func() {
Expect(err).ToNot(HaveOccurred())
defer conn.Close()
tr := &quic.Transport{Conn: conn}
addTracer(tr)
server, err := tr.Listen(
getTLSConfig(),
getQuicConfig(nil),
@ -166,6 +169,7 @@ var _ = Describe("Multiplexing", func() {
Expect(err).ToNot(HaveOccurred())
defer conn1.Close()
tr1 := &quic.Transport{Conn: conn1}
addTracer(tr1)
addr2, err := net.ResolveUDPAddr("udp", "localhost:0")
Expect(err).ToNot(HaveOccurred())
@ -173,6 +177,7 @@ var _ = Describe("Multiplexing", func() {
Expect(err).ToNot(HaveOccurred())
defer conn2.Close()
tr2 := &quic.Transport{Conn: conn2}
addTracer(tr2)
server1, err := tr1.Listen(
getTLSConfig(),
@ -219,6 +224,7 @@ var _ = Describe("Multiplexing", func() {
Expect(err).ToNot(HaveOccurred())
defer conn1.Close()
tr1 := &quic.Transport{Conn: conn1}
addTracer(tr1)
addr2, err := net.ResolveUDPAddr("udp", "localhost:0")
Expect(err).ToNot(HaveOccurred())
@ -226,6 +232,7 @@ var _ = Describe("Multiplexing", func() {
Expect(err).ToNot(HaveOccurred())
defer conn2.Close()
tr2 := &quic.Transport{Conn: conn2}
addTracer(tr2)
server, err := tr1.Listen(getTLSConfig(), getQuicConfig(nil))
Expect(err).ToNot(HaveOccurred())
@ -250,6 +257,9 @@ var _ = Describe("Multiplexing", func() {
b := make([]byte, packetLen)
rand.Read(b[1:]) // keep the first byte set to 0, so it's not classified as a QUIC packet
_, err := tr1.WriteTo(b, tr2.Conn.LocalAddr())
if ctx.Err() != nil { // ctx canceled while Read was executing
return
}
Expect(err).ToNot(HaveOccurred())
sentPackets.Add(1)
}

View file

@ -0,0 +1,90 @@
package self_test
import (
"context"
"os"
"path"
"regexp"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
quic "github.com/refraction-networking/uquic"
"github.com/refraction-networking/uquic/qlog"
)
var _ = Describe("qlog dir tests", Serial, func() {
var originalQlogDirValue string
var tempTestDirPath string
BeforeEach(func() {
originalQlogDirValue = os.Getenv("QLOGDIR")
var err error
tempTestDirPath, err = os.MkdirTemp("", "temp_test_dir")
Expect(err).ToNot(HaveOccurred())
})
AfterEach(func() {
err := os.Setenv("QLOGDIR", originalQlogDirValue)
Expect(err).ToNot(HaveOccurred())
err = os.RemoveAll(tempTestDirPath)
Expect(err).ToNot(HaveOccurred())
})
handshake := func() {
serverStopped := make(chan struct{})
server, err := quic.ListenAddr(
"localhost:0",
getTLSConfig(),
&quic.Config{
Tracer: qlog.DefaultTracer,
},
)
Expect(err).ToNot(HaveOccurred())
go func() {
defer GinkgoRecover()
defer close(serverStopped)
for {
if _, err := server.Accept(context.Background()); err != nil {
return
}
}
}()
conn, err := quic.DialAddr(
context.Background(),
server.Addr().String(),
getTLSClientConfig(),
&quic.Config{
Tracer: qlog.DefaultTracer,
},
)
Expect(err).ToNot(HaveOccurred())
conn.CloseWithError(0, "")
server.Close()
<-serverStopped
}
It("environment variable is set", func() {
qlogDir := path.Join(tempTestDirPath, "qlogs")
err := os.Setenv("QLOGDIR", qlogDir)
Expect(err).ToNot(HaveOccurred())
handshake()
_, err = os.Stat(tempTestDirPath)
qlogDirCreated := !os.IsNotExist(err)
Expect(qlogDirCreated).To(BeTrue())
childs, err := os.ReadDir(qlogDir)
Expect(err).ToNot(HaveOccurred())
Expect(len(childs)).To(Equal(2))
odcids := make([]string, 0)
vantagePoints := make([]string, 0)
qlogFileNameRegexp := regexp.MustCompile(`^([0-f]+)_(client|server).qlog$`)
for _, child := range childs {
matches := qlogFileNameRegexp.FindStringSubmatch(child.Name())
odcids = append(odcids, matches[1])
vantagePoints = append(vantagePoints, matches[2])
}
Expect(odcids[0]).To(Equal(odcids[1]))
Expect(vantagePoints).To(ContainElements("client", "server"))
})
})

View file

@ -52,22 +52,23 @@ var _ = Describe("TLS session resumption", func() {
cache := newClientSessionCache(tls.NewLRUClientSessionCache(10), gets, puts)
tlsConf := getTLSClientConfig()
tlsConf.ClientSessionCache = cache
conn, err := quic.DialAddr(
conn1, err := quic.DialAddr(
context.Background(),
fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
tlsConf,
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
defer conn1.CloseWithError(0, "")
var sessionKey string
Eventually(puts).Should(Receive(&sessionKey))
Expect(conn.ConnectionState().TLS.DidResume).To(BeFalse())
Expect(conn1.ConnectionState().TLS.DidResume).To(BeFalse())
serverConn, err := server.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())
Expect(serverConn.ConnectionState().TLS.DidResume).To(BeFalse())
conn, err = quic.DialAddr(
conn2, err := quic.DialAddr(
context.Background(),
fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
tlsConf,
@ -75,11 +76,12 @@ var _ = Describe("TLS session resumption", func() {
)
Expect(err).ToNot(HaveOccurred())
Expect(gets).To(Receive(Equal(sessionKey)))
Expect(conn.ConnectionState().TLS.DidResume).To(BeTrue())
Expect(conn2.ConnectionState().TLS.DidResume).To(BeTrue())
serverConn, err = server.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())
Expect(serverConn.ConnectionState().TLS.DidResume).To(BeTrue())
conn2.CloseWithError(0, "")
})
It("doesn't use session resumption, if the config disables it", func() {
@ -94,15 +96,16 @@ var _ = Describe("TLS session resumption", func() {
cache := newClientSessionCache(tls.NewLRUClientSessionCache(10), gets, puts)
tlsConf := getTLSClientConfig()
tlsConf.ClientSessionCache = cache
conn, err := quic.DialAddr(
conn1, err := quic.DialAddr(
context.Background(),
fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
tlsConf,
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
defer conn1.CloseWithError(0, "")
Consistently(puts).ShouldNot(Receive())
Expect(conn.ConnectionState().TLS.DidResume).To(BeFalse())
Expect(conn1.ConnectionState().TLS.DidResume).To(BeFalse())
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
@ -110,14 +113,15 @@ var _ = Describe("TLS session resumption", func() {
Expect(err).ToNot(HaveOccurred())
Expect(serverConn.ConnectionState().TLS.DidResume).To(BeFalse())
conn, err = quic.DialAddr(
conn2, err := quic.DialAddr(
context.Background(),
fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
tlsConf,
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
Expect(conn.ConnectionState().TLS.DidResume).To(BeFalse())
Expect(conn2.ConnectionState().TLS.DidResume).To(BeFalse())
defer conn2.CloseWithError(0, "")
serverConn, err = server.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())
@ -142,7 +146,7 @@ var _ = Describe("TLS session resumption", func() {
cache := newClientSessionCache(tls.NewLRUClientSessionCache(10), gets, puts)
tlsConf := getTLSClientConfig()
tlsConf.ClientSessionCache = cache
conn, err := quic.DialAddr(
conn1, err := quic.DialAddr(
context.Background(),
fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
tlsConf,
@ -150,7 +154,8 @@ var _ = Describe("TLS session resumption", func() {
)
Expect(err).ToNot(HaveOccurred())
Consistently(puts).ShouldNot(Receive())
Expect(conn.ConnectionState().TLS.DidResume).To(BeFalse())
Expect(conn1.ConnectionState().TLS.DidResume).To(BeFalse())
defer conn1.CloseWithError(0, "")
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
@ -158,14 +163,15 @@ var _ = Describe("TLS session resumption", func() {
Expect(err).ToNot(HaveOccurred())
Expect(serverConn.ConnectionState().TLS.DidResume).To(BeFalse())
conn, err = quic.DialAddr(
conn2, err := quic.DialAddr(
context.Background(),
fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
tlsConf,
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
Expect(conn.ConnectionState().TLS.DidResume).To(BeFalse())
Expect(conn2.ConnectionState().TLS.DidResume).To(BeFalse())
defer conn2.CloseWithError(0, "")
serverConn, err = server.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())

View file

@ -87,10 +87,9 @@ var (
logBuf *syncedBuffer
versionParam string
qlogTracer func(context.Context, logging.Perspective, quic.ConnectionID) *logging.ConnectionTracer
enableQlog bool
version quic.VersionNumber
version quic.Version
tlsConfig *tls.Config
tlsConfigLongChain *tls.Config
tlsClientConfig *tls.Config
@ -139,9 +138,6 @@ func init() {
}
var _ = BeforeSuite(func() {
if enableQlog {
qlogTracer = tools.NewQlogger(GinkgoWriter)
}
switch versionParam {
case "1":
version = quic.Version1
@ -151,7 +147,7 @@ var _ = BeforeSuite(func() {
Fail(fmt.Sprintf("unknown QUIC version: %s", versionParam))
}
fmt.Printf("Using QUIC version: %s\n", version)
protocol.SupportedVersions = []quic.VersionNumber{version}
protocol.SupportedVersions = []quic.Version{version}
})
func getTLSConfig() *tls.Config {
@ -176,28 +172,48 @@ func getQuicConfig(conf *quic.Config) *quic.Config {
} else {
conf = conf.Clone()
}
if enableQlog {
if conf.Tracer == nil {
conf.Tracer = func(ctx context.Context, p logging.Perspective, connID quic.ConnectionID) *logging.ConnectionTracer {
return logging.NewMultiplexedConnectionTracer(
qlogTracer(ctx, p, connID),
// multiplex it with an empty tracer to check that we're correctly ignoring unset callbacks everywhere
&logging.ConnectionTracer{},
)
}
} else if qlogTracer != nil {
origTracer := conf.Tracer
conf.Tracer = func(ctx context.Context, p logging.Perspective, connID quic.ConnectionID) *logging.ConnectionTracer {
return logging.NewMultiplexedConnectionTracer(
qlogTracer(ctx, p, connID),
origTracer(ctx, p, connID),
)
}
if !enableQlog {
return conf
}
if conf.Tracer == nil {
conf.Tracer = func(ctx context.Context, p logging.Perspective, connID quic.ConnectionID) *logging.ConnectionTracer {
return logging.NewMultiplexedConnectionTracer(
tools.NewQlogConnectionTracer(GinkgoWriter)(ctx, p, connID),
// multiplex it with an empty tracer to check that we're correctly ignoring unset callbacks everywhere
&logging.ConnectionTracer{},
)
}
return conf
}
origTracer := conf.Tracer
conf.Tracer = func(ctx context.Context, p logging.Perspective, connID quic.ConnectionID) *logging.ConnectionTracer {
return logging.NewMultiplexedConnectionTracer(
tools.NewQlogConnectionTracer(GinkgoWriter)(ctx, p, connID),
origTracer(ctx, p, connID),
)
}
return conf
}
func addTracer(tr *quic.Transport) {
if !enableQlog {
return
}
if tr.Tracer == nil {
tr.Tracer = logging.NewMultiplexedTracer(
tools.QlogTracer(GinkgoWriter),
// multiplex it with an empty tracer to check that we're correctly ignoring unset callbacks everywhere
&logging.Tracer{},
)
return
}
origTracer := tr.Tracer
tr.Tracer = logging.NewMultiplexedTracer(
tools.QlogTracer(GinkgoWriter),
origTracer,
)
}
var _ = BeforeEach(func() {
log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds)

View file

@ -22,12 +22,12 @@ type faultyConn struct {
net.PacketConn
MaxPackets int32
counter int32
counter atomic.Int32
}
func (c *faultyConn) ReadFrom(p []byte) (int, net.Addr, error) {
n, addr, err := c.PacketConn.ReadFrom(p)
counter := atomic.AddInt32(&c.counter, 1)
counter := c.counter.Add(1)
if counter <= c.MaxPackets {
return n, addr, err
}
@ -35,7 +35,7 @@ func (c *faultyConn) ReadFrom(p []byte) (int, net.Addr, error) {
}
func (c *faultyConn) WriteTo(p []byte, addr net.Addr) (int, error) {
counter := atomic.AddInt32(&c.counter, 1)
counter := c.counter.Add(1)
if counter <= c.MaxPackets {
return c.PacketConn.WriteTo(p, addr)
}
@ -185,11 +185,13 @@ var _ = Describe("Timeout tests", func() {
Expect(err).ToNot(HaveOccurred())
defer server.Close()
serverConnChan := make(chan quic.Connection, 1)
serverConnClosed := make(chan struct{})
go func() {
defer GinkgoRecover()
conn, err := server.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())
serverConnChan <- conn
conn.AcceptStream(context.Background()) // blocks until the connection is closed
close(serverConnClosed)
}()
@ -240,7 +242,7 @@ var _ = Describe("Timeout tests", func() {
Consistently(serverConnClosed).ShouldNot(BeClosed())
// make the go routine return
Expect(server.Close()).To(Succeed())
(<-serverConnChan).CloseWithError(0, "")
Eventually(serverConnClosed).Should(BeClosed())
})
@ -266,11 +268,13 @@ var _ = Describe("Timeout tests", func() {
Expect(err).ToNot(HaveOccurred())
defer proxy.Close()
serverConnChan := make(chan quic.Connection, 1)
serverConnClosed := make(chan struct{})
go func() {
defer GinkgoRecover()
conn, err := server.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())
serverConnChan <- conn
<-conn.Context().Done() // block until the connection is closed
close(serverConnClosed)
}()
@ -309,7 +313,7 @@ var _ = Describe("Timeout tests", func() {
Consistently(serverConnClosed).ShouldNot(BeClosed())
// make the go routine return
Expect(server.Close()).To(Succeed())
(<-serverConnChan).CloseWithError(0, "")
Eventually(serverConnClosed).Should(BeClosed())
})
})
@ -325,11 +329,13 @@ var _ = Describe("Timeout tests", func() {
Expect(err).ToNot(HaveOccurred())
defer server.Close()
serverConnChan := make(chan quic.Connection, 1)
serverConnClosed := make(chan struct{})
go func() {
defer GinkgoRecover()
conn, err := server.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())
serverConnChan <- conn
conn.AcceptStream(context.Background()) // blocks until the connection is closed
close(serverConnClosed)
}()
@ -370,7 +376,7 @@ var _ = Describe("Timeout tests", func() {
_, err = str.Write([]byte("foobar"))
checkTimeoutError(err)
Expect(server.Close()).To(Succeed())
(<-serverConnChan).CloseWithError(0, "")
Eventually(serverConnClosed).Should(BeClosed())
})

View file

@ -19,7 +19,7 @@ import (
. "github.com/onsi/gomega"
)
var _ = Describe("Handshake tests", func() {
var _ = Describe("Tracer tests", func() {
addTracers := func(pers protocol.Perspective, conf *quic.Config) *quic.Config {
enableQlog := mrand.Int()%3 != 0
enableCustomTracer := mrand.Int()%3 != 0
@ -30,10 +30,10 @@ var _ = Describe("Handshake tests", func() {
if enableQlog {
tracerConstructors = append(tracerConstructors, func(_ context.Context, p logging.Perspective, connID quic.ConnectionID) *logging.ConnectionTracer {
if mrand.Int()%2 == 0 { // simulate that a qlog collector might only want to log some connections
fmt.Fprintf(GinkgoWriter, "%s qlog tracer deciding to not trace connection %x\n", p, connID)
fmt.Fprintf(GinkgoWriter, "%s qlog tracer deciding to not trace connection %s\n", p, connID)
return nil
}
fmt.Fprintf(GinkgoWriter, "%s qlog tracing connection %x\n", p, connID)
fmt.Fprintf(GinkgoWriter, "%s qlog tracing connection %s\n", p, connID)
return qlog.NewConnectionTracer(utils.NewBufferedWriteCloser(bufio.NewWriter(&bytes.Buffer{}), io.NopCloser(nil)), p, connID)
})
}

View file

@ -142,5 +142,6 @@ var _ = Describe("Unidirectional Streams", func() {
runReceivingPeer(client)
<-done1
<-done2
client.CloseWithError(0, "")
})
})

View file

@ -1,916 +0,0 @@
//go:build !go1.21
package self_test
import (
"context"
"fmt"
"io"
mrand "math/rand"
"net"
"sync"
"sync/atomic"
"time"
quic "github.com/refraction-networking/uquic"
tls "github.com/refraction-networking/utls"
quicproxy "github.com/refraction-networking/uquic/integrationtests/tools/proxy"
"github.com/refraction-networking/uquic/internal/protocol"
"github.com/refraction-networking/uquic/internal/wire"
"github.com/refraction-networking/uquic/logging"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("0-RTT", func() {
rtt := scaleDuration(5 * time.Millisecond)
runCountingProxy := func(serverPort int) (*quicproxy.QuicProxy, *uint32) {
var num0RTTPackets uint32 // to be used as an atomic
proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
RemoteAddr: fmt.Sprintf("localhost:%d", serverPort),
DelayPacket: func(_ quicproxy.Direction, data []byte) time.Duration {
for len(data) > 0 {
if !wire.IsLongHeaderPacket(data[0]) {
break
}
hdr, _, rest, err := wire.ParsePacket(data)
Expect(err).ToNot(HaveOccurred())
if hdr.Type == protocol.PacketType0RTT {
atomic.AddUint32(&num0RTTPackets, 1)
break
}
data = rest
}
return rtt / 2
},
})
Expect(err).ToNot(HaveOccurred())
return proxy, &num0RTTPackets
}
dialAndReceiveSessionTicket := func(serverConf *quic.Config) (*tls.Config, *tls.Config) {
tlsConf := getTLSConfig()
if serverConf == nil {
serverConf = getQuicConfig(nil)
}
serverConf.Allow0RTT = true
ln, err := quic.ListenAddrEarly(
"localhost:0",
tlsConf,
serverConf,
)
Expect(err).ToNot(HaveOccurred())
defer ln.Close()
proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
RemoteAddr: fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port),
DelayPacket: func(_ quicproxy.Direction, data []byte) time.Duration { return rtt / 2 },
})
Expect(err).ToNot(HaveOccurred())
defer proxy.Close()
// dial the first connection in order to receive a session ticket
done := make(chan struct{})
go func() {
defer GinkgoRecover()
defer close(done)
conn, err := ln.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())
<-conn.Context().Done()
}()
clientConf := getTLSClientConfig()
gets := make(chan string, 100)
puts := make(chan string, 100)
clientConf.ClientSessionCache = newClientSessionCache(tls.NewLRUClientSessionCache(100), gets, puts)
conn, err := quic.DialAddr(
context.Background(),
fmt.Sprintf("localhost:%d", proxy.LocalPort()),
clientConf,
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
Eventually(puts).Should(Receive())
// received the session ticket. We're done here.
Expect(conn.CloseWithError(0, "")).To(Succeed())
Eventually(done).Should(BeClosed())
return tlsConf, clientConf
}
transfer0RTTData := func(
ln *quic.EarlyListener,
proxyPort int,
connIDLen int,
clientTLSConf *tls.Config,
clientConf *quic.Config,
testdata []byte, // data to transfer
) {
// accept the second connection, and receive the data sent in 0-RTT
done := make(chan struct{})
go func() {
defer GinkgoRecover()
conn, err := ln.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())
str, err := conn.AcceptStream(context.Background())
Expect(err).ToNot(HaveOccurred())
data, err := io.ReadAll(str)
Expect(err).ToNot(HaveOccurred())
Expect(data).To(Equal(testdata))
Expect(str.Close()).To(Succeed())
Expect(conn.ConnectionState().Used0RTT).To(BeTrue())
<-conn.Context().Done()
close(done)
}()
if clientConf == nil {
clientConf = getQuicConfig(nil)
}
var conn quic.EarlyConnection
if connIDLen == 0 {
var err error
conn, err = quic.DialAddrEarly(
context.Background(),
fmt.Sprintf("localhost:%d", proxyPort),
clientTLSConf,
clientConf,
)
Expect(err).ToNot(HaveOccurred())
} else {
addr, err := net.ResolveUDPAddr("udp", "localhost:0")
Expect(err).ToNot(HaveOccurred())
udpConn, err := net.ListenUDP("udp", addr)
Expect(err).ToNot(HaveOccurred())
defer udpConn.Close()
tr := &quic.Transport{
Conn: udpConn,
ConnectionIDLength: connIDLen,
}
defer tr.Close()
conn, err = tr.DialEarly(
context.Background(),
&net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: proxyPort},
clientTLSConf,
clientConf,
)
Expect(err).ToNot(HaveOccurred())
}
defer conn.CloseWithError(0, "")
str, err := conn.OpenStream()
Expect(err).ToNot(HaveOccurred())
_, err = str.Write(testdata)
Expect(err).ToNot(HaveOccurred())
Expect(str.Close()).To(Succeed())
<-conn.HandshakeComplete()
Expect(conn.ConnectionState().Used0RTT).To(BeTrue())
io.ReadAll(str) // wait for the EOF from the server to arrive before closing the conn
conn.CloseWithError(0, "")
Eventually(done).Should(BeClosed())
Eventually(conn.Context().Done()).Should(BeClosed())
}
check0RTTRejected := func(
ln *quic.EarlyListener,
proxyPort int,
clientConf *tls.Config,
) {
conn, err := quic.DialAddrEarly(
context.Background(),
fmt.Sprintf("localhost:%d", proxyPort),
clientConf,
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
str, err := conn.OpenUniStream()
Expect(err).ToNot(HaveOccurred())
_, err = str.Write(make([]byte, 3000))
Expect(err).ToNot(HaveOccurred())
Expect(str.Close()).To(Succeed())
Expect(conn.ConnectionState().Used0RTT).To(BeFalse())
// make sure the server doesn't process the data
ctx, cancel := context.WithTimeout(context.Background(), scaleDuration(50*time.Millisecond))
defer cancel()
serverConn, err := ln.Accept(ctx)
Expect(err).ToNot(HaveOccurred())
Expect(serverConn.ConnectionState().Used0RTT).To(BeFalse())
_, err = serverConn.AcceptUniStream(ctx)
Expect(err).To(Equal(context.DeadlineExceeded))
Expect(serverConn.CloseWithError(0, "")).To(Succeed())
Eventually(conn.Context().Done()).Should(BeClosed())
}
// can be used to extract 0-RTT from a packetCounter
get0RTTPackets := func(packets []packet) []protocol.PacketNumber {
var zeroRTTPackets []protocol.PacketNumber
for _, p := range packets {
if p.hdr.Type == protocol.PacketType0RTT {
zeroRTTPackets = append(zeroRTTPackets, p.hdr.PacketNumber)
}
}
return zeroRTTPackets
}
for _, l := range []int{0, 15} {
connIDLen := l
It(fmt.Sprintf("transfers 0-RTT data, with %d byte connection IDs", connIDLen), func() {
tlsConf, clientTLSConf := dialAndReceiveSessionTicket(nil)
counter, tracer := newPacketTracer()
ln, err := quic.ListenAddrEarly(
"localhost:0",
tlsConf,
getQuicConfig(&quic.Config{
Allow0RTT: true,
Tracer: newTracer(tracer),
}),
)
Expect(err).ToNot(HaveOccurred())
defer ln.Close()
proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
defer proxy.Close()
transfer0RTTData(
ln,
proxy.LocalPort(),
connIDLen,
clientTLSConf,
getQuicConfig(nil),
PRData,
)
var numNewConnIDs int
for _, p := range counter.getRcvdLongHeaderPackets() {
for _, f := range p.frames {
if _, ok := f.(*logging.NewConnectionIDFrame); ok {
numNewConnIDs++
}
}
}
if connIDLen == 0 {
Expect(numNewConnIDs).To(BeZero())
} else {
Expect(numNewConnIDs).ToNot(BeZero())
}
num0RTT := atomic.LoadUint32(num0RTTPackets)
fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
Expect(num0RTT).ToNot(BeZero())
zeroRTTPackets := get0RTTPackets(counter.getRcvdLongHeaderPackets())
Expect(len(zeroRTTPackets)).To(BeNumerically(">", 10))
Expect(zeroRTTPackets).To(ContainElement(protocol.PacketNumber(0)))
})
}
// Test that data intended to be sent with 1-RTT protection is not sent in 0-RTT packets.
It("waits for a connection until the handshake is done", func() {
tlsConf, clientConf := dialAndReceiveSessionTicket(nil)
zeroRTTData := GeneratePRData(5 << 10)
oneRTTData := PRData
counter, tracer := newPacketTracer()
ln, err := quic.ListenAddrEarly(
"localhost:0",
tlsConf,
getQuicConfig(&quic.Config{
Allow0RTT: true,
Tracer: newTracer(tracer),
}),
)
Expect(err).ToNot(HaveOccurred())
defer ln.Close()
// now accept the second connection, and receive the 0-RTT data
go func() {
defer GinkgoRecover()
conn, err := ln.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())
str, err := conn.AcceptUniStream(context.Background())
Expect(err).ToNot(HaveOccurred())
data, err := io.ReadAll(str)
Expect(err).ToNot(HaveOccurred())
Expect(data).To(Equal(zeroRTTData))
str, err = conn.AcceptUniStream(context.Background())
Expect(err).ToNot(HaveOccurred())
data, err = io.ReadAll(str)
Expect(err).ToNot(HaveOccurred())
Expect(data).To(Equal(oneRTTData))
Expect(conn.CloseWithError(0, "")).To(Succeed())
}()
proxy, _ := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
defer proxy.Close()
conn, err := quic.DialAddrEarly(
context.Background(),
fmt.Sprintf("localhost:%d", proxy.LocalPort()),
clientConf,
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
firstStr, err := conn.OpenUniStream()
Expect(err).ToNot(HaveOccurred())
_, err = firstStr.Write(zeroRTTData)
Expect(err).ToNot(HaveOccurred())
Expect(firstStr.Close()).To(Succeed())
// wait for the handshake to complete
Eventually(conn.HandshakeComplete()).Should(BeClosed())
str, err := conn.OpenUniStream()
Expect(err).ToNot(HaveOccurred())
_, err = str.Write(PRData)
Expect(err).ToNot(HaveOccurred())
Expect(str.Close()).To(Succeed())
<-conn.Context().Done()
// check that 0-RTT packets only contain STREAM frames for the first stream
var num0RTT int
for _, p := range counter.getRcvdLongHeaderPackets() {
if p.hdr.Header.Type != protocol.PacketType0RTT {
continue
}
for _, f := range p.frames {
sf, ok := f.(*logging.StreamFrame)
if !ok {
continue
}
num0RTT++
Expect(sf.StreamID).To(Equal(firstStr.StreamID()))
}
}
fmt.Fprintf(GinkgoWriter, "received %d STREAM frames in 0-RTT packets\n", num0RTT)
Expect(num0RTT).ToNot(BeZero())
})
It("transfers 0-RTT data, when 0-RTT packets are lost", func() {
var (
num0RTTPackets uint32 // to be used as an atomic
num0RTTDropped uint32
)
tlsConf, clientConf := dialAndReceiveSessionTicket(nil)
counter, tracer := newPacketTracer()
ln, err := quic.ListenAddrEarly(
"localhost:0",
tlsConf,
getQuicConfig(&quic.Config{
Allow0RTT: true,
Tracer: newTracer(tracer),
}),
)
Expect(err).ToNot(HaveOccurred())
defer ln.Close()
proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
RemoteAddr: fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port),
DelayPacket: func(_ quicproxy.Direction, data []byte) time.Duration {
if wire.IsLongHeaderPacket(data[0]) {
hdr, _, _, err := wire.ParsePacket(data)
Expect(err).ToNot(HaveOccurred())
if hdr.Type == protocol.PacketType0RTT {
atomic.AddUint32(&num0RTTPackets, 1)
}
}
return rtt / 2
},
DropPacket: func(_ quicproxy.Direction, data []byte) bool {
if !wire.IsLongHeaderPacket(data[0]) {
return false
}
hdr, _, _, err := wire.ParsePacket(data)
Expect(err).ToNot(HaveOccurred())
if hdr.Type == protocol.PacketType0RTT {
// drop 25% of the 0-RTT packets
drop := mrand.Intn(4) == 0
if drop {
atomic.AddUint32(&num0RTTDropped, 1)
}
return drop
}
return false
},
})
Expect(err).ToNot(HaveOccurred())
defer proxy.Close()
transfer0RTTData(ln, proxy.LocalPort(), protocol.DefaultConnectionIDLength, clientConf, nil, PRData)
num0RTT := atomic.LoadUint32(&num0RTTPackets)
numDropped := atomic.LoadUint32(&num0RTTDropped)
fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets. Dropped %d of those.", num0RTT, numDropped)
Expect(numDropped).ToNot(BeZero())
Expect(num0RTT).ToNot(BeZero())
Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).ToNot(BeEmpty())
})
It("retransmits all 0-RTT data when the server performs a Retry", func() {
var mutex sync.Mutex
var firstConnID, secondConnID *protocol.ConnectionID
var firstCounter, secondCounter protocol.ByteCount
tlsConf, clientConf := dialAndReceiveSessionTicket(nil)
countZeroRTTBytes := func(data []byte) (n protocol.ByteCount) {
for len(data) > 0 {
hdr, _, rest, err := wire.ParsePacket(data)
if err != nil {
return
}
data = rest
if hdr.Type == protocol.PacketType0RTT {
n += hdr.Length - 16 /* AEAD tag */
}
}
return
}
counter, tracer := newPacketTracer()
ln, err := quic.ListenAddrEarly(
"localhost:0",
tlsConf,
getQuicConfig(&quic.Config{
RequireAddressValidation: func(net.Addr) bool { return true },
Allow0RTT: true,
Tracer: newTracer(tracer),
}),
)
Expect(err).ToNot(HaveOccurred())
defer ln.Close()
proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
RemoteAddr: fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port),
DelayPacket: func(dir quicproxy.Direction, data []byte) time.Duration {
connID, err := wire.ParseConnectionID(data, 0)
Expect(err).ToNot(HaveOccurred())
mutex.Lock()
defer mutex.Unlock()
if zeroRTTBytes := countZeroRTTBytes(data); zeroRTTBytes > 0 {
if firstConnID == nil {
firstConnID = &connID
firstCounter += zeroRTTBytes
} else if firstConnID != nil && *firstConnID == connID {
Expect(secondConnID).To(BeNil())
firstCounter += zeroRTTBytes
} else if secondConnID == nil {
secondConnID = &connID
secondCounter += zeroRTTBytes
} else if secondConnID != nil && *secondConnID == connID {
secondCounter += zeroRTTBytes
} else {
Fail("received 3 connection IDs on 0-RTT packets")
}
}
return rtt / 2
},
})
Expect(err).ToNot(HaveOccurred())
defer proxy.Close()
transfer0RTTData(ln, proxy.LocalPort(), protocol.DefaultConnectionIDLength, clientConf, nil, GeneratePRData(5000)) // ~5 packets
mutex.Lock()
defer mutex.Unlock()
Expect(firstCounter).To(BeNumerically("~", 5000+100 /* framing overhead */, 100)) // the FIN bit might be sent extra
Expect(secondCounter).To(BeNumerically("~", firstCounter, 20))
zeroRTTPackets := get0RTTPackets(counter.getRcvdLongHeaderPackets())
Expect(len(zeroRTTPackets)).To(BeNumerically(">=", 5))
Expect(zeroRTTPackets[0]).To(BeNumerically(">=", protocol.PacketNumber(5)))
})
It("doesn't reject 0-RTT when the server's transport stream limit increased", func() {
const maxStreams = 1
tlsConf, clientConf := dialAndReceiveSessionTicket(getQuicConfig(&quic.Config{
MaxIncomingUniStreams: maxStreams,
}))
ln, err := quic.ListenAddrEarly(
"localhost:0",
tlsConf,
getQuicConfig(&quic.Config{
MaxIncomingUniStreams: maxStreams + 1,
Allow0RTT: true,
}),
)
Expect(err).ToNot(HaveOccurred())
defer ln.Close()
proxy, _ := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
defer proxy.Close()
conn, err := quic.DialAddrEarly(
context.Background(),
fmt.Sprintf("localhost:%d", proxy.LocalPort()),
clientConf,
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
str, err := conn.OpenUniStream()
Expect(err).ToNot(HaveOccurred())
_, err = str.Write([]byte("foobar"))
Expect(err).ToNot(HaveOccurred())
Expect(str.Close()).To(Succeed())
// The client remembers the old limit and refuses to open a new stream.
_, err = conn.OpenUniStream()
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("too many open streams"))
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
_, err = conn.OpenUniStreamSync(ctx)
Expect(err).ToNot(HaveOccurred())
Expect(conn.ConnectionState().Used0RTT).To(BeTrue())
Expect(conn.CloseWithError(0, "")).To(Succeed())
})
It("rejects 0-RTT when the server's stream limit decreased", func() {
const maxStreams = 42
tlsConf, clientConf := dialAndReceiveSessionTicket(getQuicConfig(&quic.Config{
MaxIncomingStreams: maxStreams,
}))
counter, tracer := newPacketTracer()
ln, err := quic.ListenAddrEarly(
"localhost:0",
tlsConf,
getQuicConfig(&quic.Config{
MaxIncomingStreams: maxStreams - 1,
Allow0RTT: true,
Tracer: newTracer(tracer),
}),
)
Expect(err).ToNot(HaveOccurred())
defer ln.Close()
proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
defer proxy.Close()
check0RTTRejected(ln, proxy.LocalPort(), clientConf)
// The client should send 0-RTT packets, but the server doesn't process them.
num0RTT := atomic.LoadUint32(num0RTTPackets)
fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
Expect(num0RTT).ToNot(BeZero())
Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty())
})
It("rejects 0-RTT when the ALPN changed", func() {
tlsConf, clientConf := dialAndReceiveSessionTicket(nil)
// now close the listener and dial new connection with a different ALPN
// clientConf.NextProtos = []string{"new-alpn"}
clientConf.NextProtos = append(clientConf.NextProtos, "new-alpn")
tlsConf.NextProtos = []string{"new-alpn"}
counter, tracer := newPacketTracer()
ln, err := quic.ListenAddrEarly(
"localhost:0",
tlsConf,
getQuicConfig(&quic.Config{
Allow0RTT: true,
Tracer: newTracer(tracer),
}),
)
Expect(err).ToNot(HaveOccurred())
defer ln.Close()
proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
defer proxy.Close()
check0RTTRejected(ln, proxy.LocalPort(), clientConf)
// The client should send 0-RTT packets, but the server doesn't process them.
num0RTT := atomic.LoadUint32(num0RTTPackets)
fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
Expect(num0RTT).ToNot(BeZero())
Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty())
})
It("rejects 0-RTT when the application doesn't allow it", func() {
tlsConf, clientConf := dialAndReceiveSessionTicket(nil)
// now close the listener and dial new connection with a different ALPN
counter, tracer := newPacketTracer()
ln, err := quic.ListenAddrEarly(
"localhost:0",
tlsConf,
getQuicConfig(&quic.Config{
Allow0RTT: false, // application rejects 0-RTT
Tracer: newTracer(tracer),
}),
)
Expect(err).ToNot(HaveOccurred())
defer ln.Close()
proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
defer proxy.Close()
check0RTTRejected(ln, proxy.LocalPort(), clientConf)
// The client should send 0-RTT packets, but the server doesn't process them.
num0RTT := atomic.LoadUint32(num0RTTPackets)
fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
Expect(num0RTT).ToNot(BeZero())
Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty())
})
DescribeTable("flow control limits",
func(addFlowControlLimit func(*quic.Config, uint64)) {
counter, tracer := newPacketTracer()
firstConf := getQuicConfig(&quic.Config{Allow0RTT: true})
addFlowControlLimit(firstConf, 3)
tlsConf, clientConf := dialAndReceiveSessionTicket(firstConf)
secondConf := getQuicConfig(&quic.Config{
Allow0RTT: true,
Tracer: newTracer(tracer),
})
addFlowControlLimit(secondConf, 100)
ln, err := quic.ListenAddrEarly(
"localhost:0",
tlsConf,
secondConf,
)
Expect(err).ToNot(HaveOccurred())
defer ln.Close()
proxy, _ := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
defer proxy.Close()
conn, err := quic.DialAddrEarly(
context.Background(),
fmt.Sprintf("localhost:%d", proxy.LocalPort()),
clientConf,
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
str, err := conn.OpenUniStream()
Expect(err).ToNot(HaveOccurred())
written := make(chan struct{})
go func() {
defer GinkgoRecover()
defer close(written)
_, err := str.Write([]byte("foobar"))
Expect(err).ToNot(HaveOccurred())
Expect(str.Close()).To(Succeed())
}()
Eventually(written).Should(BeClosed())
serverConn, err := ln.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())
rstr, err := serverConn.AcceptUniStream(context.Background())
Expect(err).ToNot(HaveOccurred())
data, err := io.ReadAll(rstr)
Expect(err).ToNot(HaveOccurred())
Expect(data).To(Equal([]byte("foobar")))
Expect(serverConn.ConnectionState().Used0RTT).To(BeTrue())
Expect(serverConn.CloseWithError(0, "")).To(Succeed())
Eventually(conn.Context().Done()).Should(BeClosed())
var processedFirst bool
for _, p := range counter.getRcvdLongHeaderPackets() {
for _, f := range p.frames {
if sf, ok := f.(*logging.StreamFrame); ok {
if !processedFirst {
// The first STREAM should have been sent in a 0-RTT packet.
// Due to the flow control limit, the STREAM frame was limit to the first 3 bytes.
Expect(p.hdr.Type).To(Equal(protocol.PacketType0RTT))
Expect(sf.Length).To(BeEquivalentTo(3))
processedFirst = true
} else {
Fail("STREAM was shouldn't have been sent in 0-RTT")
}
}
}
}
},
Entry("doesn't reject 0-RTT when the server's transport stream flow control limit increased", func(c *quic.Config, limit uint64) { c.InitialStreamReceiveWindow = limit }),
Entry("doesn't reject 0-RTT when the server's transport connection flow control limit increased", func(c *quic.Config, limit uint64) { c.InitialConnectionReceiveWindow = limit }),
)
for _, l := range []int{0, 15} {
connIDLen := l
It(fmt.Sprintf("correctly deals with 0-RTT rejections, for %d byte connection IDs", connIDLen), func() {
tlsConf, clientConf := dialAndReceiveSessionTicket(nil)
// now dial new connection with different transport parameters
counter, tracer := newPacketTracer()
ln, err := quic.ListenAddrEarly(
"localhost:0",
tlsConf,
getQuicConfig(&quic.Config{
MaxIncomingUniStreams: 1,
Tracer: newTracer(tracer),
}),
)
Expect(err).ToNot(HaveOccurred())
defer ln.Close()
proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
defer proxy.Close()
conn, err := quic.DialAddrEarly(
context.Background(),
fmt.Sprintf("localhost:%d", proxy.LocalPort()),
clientConf,
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
// The client remembers that it was allowed to open 2 uni-directional streams.
firstStr, err := conn.OpenUniStream()
Expect(err).ToNot(HaveOccurred())
written := make(chan struct{}, 2)
go func() {
defer GinkgoRecover()
defer func() { written <- struct{}{} }()
_, err := firstStr.Write([]byte("first flight"))
Expect(err).ToNot(HaveOccurred())
}()
secondStr, err := conn.OpenUniStream()
Expect(err).ToNot(HaveOccurred())
go func() {
defer GinkgoRecover()
defer func() { written <- struct{}{} }()
_, err := secondStr.Write([]byte("first flight"))
Expect(err).ToNot(HaveOccurred())
}()
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
_, err = conn.AcceptStream(ctx)
Expect(err).To(MatchError(quic.Err0RTTRejected))
Eventually(written).Should(Receive())
Eventually(written).Should(Receive())
_, err = firstStr.Write([]byte("foobar"))
Expect(err).To(MatchError(quic.Err0RTTRejected))
_, err = conn.OpenUniStream()
Expect(err).To(MatchError(quic.Err0RTTRejected))
_, err = conn.AcceptStream(ctx)
Expect(err).To(Equal(quic.Err0RTTRejected))
newConn := conn.NextConnection()
str, err := newConn.OpenUniStream()
Expect(err).ToNot(HaveOccurred())
_, err = newConn.OpenUniStream()
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("too many open streams"))
_, err = str.Write([]byte("second flight"))
Expect(err).ToNot(HaveOccurred())
Expect(str.Close()).To(Succeed())
Expect(conn.CloseWithError(0, "")).To(Succeed())
// The client should send 0-RTT packets, but the server doesn't process them.
num0RTT := atomic.LoadUint32(num0RTTPackets)
fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
Expect(num0RTT).ToNot(BeZero())
Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty())
})
}
It("queues 0-RTT packets, if the Initial is delayed", func() {
tlsConf, clientConf := dialAndReceiveSessionTicket(nil)
counter, tracer := newPacketTracer()
ln, err := quic.ListenAddrEarly(
"localhost:0",
tlsConf,
getQuicConfig(&quic.Config{
Allow0RTT: true,
Tracer: newTracer(tracer),
}),
)
Expect(err).ToNot(HaveOccurred())
defer ln.Close()
proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
RemoteAddr: ln.Addr().String(),
DelayPacket: func(dir quicproxy.Direction, data []byte) time.Duration {
if dir == quicproxy.DirectionIncoming && wire.IsLongHeaderPacket(data[0]) && data[0]&0x30>>4 == 0 { // Initial packet from client
return rtt/2 + rtt
}
return rtt / 2
},
})
Expect(err).ToNot(HaveOccurred())
defer proxy.Close()
transfer0RTTData(ln, proxy.LocalPort(), protocol.DefaultConnectionIDLength, clientConf, nil, PRData)
Expect(counter.getRcvdLongHeaderPackets()[0].hdr.Type).To(Equal(protocol.PacketTypeInitial))
zeroRTTPackets := get0RTTPackets(counter.getRcvdLongHeaderPackets())
Expect(len(zeroRTTPackets)).To(BeNumerically(">", 10))
Expect(zeroRTTPackets[0]).To(Equal(protocol.PacketNumber(0)))
})
It("sends 0-RTT datagrams", func() {
tlsConf, clientTLSConf := dialAndReceiveSessionTicket(getQuicConfig(&quic.Config{
EnableDatagrams: true,
}))
counter, tracer := newPacketTracer()
ln, err := quic.ListenAddrEarly(
"localhost:0",
tlsConf,
getQuicConfig(&quic.Config{
Allow0RTT: true,
EnableDatagrams: true,
Tracer: newTracer(tracer),
}),
)
Expect(err).ToNot(HaveOccurred())
defer ln.Close()
proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
defer proxy.Close()
// second connection
sentMessage := GeneratePRData(100)
var receivedMessage []byte
received := make(chan struct{})
go func() {
defer GinkgoRecover()
defer close(received)
conn, err := ln.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())
receivedMessage, err = conn.ReceiveMessage(context.Background())
Expect(err).ToNot(HaveOccurred())
Expect(conn.ConnectionState().Used0RTT).To(BeTrue())
}()
conn, err := quic.DialAddrEarly(
context.Background(),
fmt.Sprintf("localhost:%d", proxy.LocalPort()),
clientTLSConf,
getQuicConfig(&quic.Config{
EnableDatagrams: true,
}),
)
Expect(err).ToNot(HaveOccurred())
Expect(conn.ConnectionState().SupportsDatagrams).To(BeTrue())
Expect(conn.SendMessage(sentMessage)).To(Succeed())
<-conn.HandshakeComplete()
<-received
Expect(conn.ConnectionState().Used0RTT).To(BeTrue())
Expect(receivedMessage).To(Equal(sentMessage))
Expect(conn.CloseWithError(0, "")).To(Succeed())
num0RTT := atomic.LoadUint32(num0RTTPackets)
fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
Expect(num0RTT).ToNot(BeZero())
zeroRTTPackets := get0RTTPackets(counter.getRcvdLongHeaderPackets())
Expect(zeroRTTPackets).To(HaveLen(1))
})
It("rejects 0-RTT datagrams when the server doesn't support datagrams anymore", func() {
tlsConf, clientTLSConf := dialAndReceiveSessionTicket(getQuicConfig(&quic.Config{
EnableDatagrams: true,
}))
counter, tracer := newPacketTracer()
ln, err := quic.ListenAddrEarly(
"localhost:0",
tlsConf,
getQuicConfig(&quic.Config{
Allow0RTT: true,
EnableDatagrams: false,
Tracer: newTracer(tracer),
}),
)
Expect(err).ToNot(HaveOccurred())
defer ln.Close()
proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
defer proxy.Close()
// second connection
go func() {
defer GinkgoRecover()
conn, err := ln.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())
_, err = conn.ReceiveMessage(context.Background())
Expect(err.Error()).To(Equal("datagram support disabled"))
<-conn.HandshakeComplete()
Expect(conn.ConnectionState().Used0RTT).To(BeFalse())
}()
conn, err := quic.DialAddrEarly(
context.Background(),
fmt.Sprintf("localhost:%d", proxy.LocalPort()),
clientTLSConf,
getQuicConfig(&quic.Config{
EnableDatagrams: true,
}),
)
Expect(err).ToNot(HaveOccurred())
// the client can temporarily send datagrams but the server doesn't process them.
Expect(conn.ConnectionState().SupportsDatagrams).To(BeTrue())
Expect(conn.SendMessage(make([]byte, 100))).To(Succeed())
<-conn.HandshakeComplete()
Expect(conn.ConnectionState().SupportsDatagrams).To(BeFalse())
Expect(conn.ConnectionState().Used0RTT).To(BeFalse())
Expect(conn.CloseWithError(0, "")).To(Succeed())
num0RTT := atomic.LoadUint32(num0RTTPackets)
fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
Expect(num0RTT).ToNot(BeZero())
Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty())
})
})

View file

@ -1,12 +1,9 @@
//go:build go1.21
package self_test
import (
"context"
"fmt"
"io"
mrand "math/rand"
"net"
"sync"
"sync/atomic"
@ -57,8 +54,8 @@ func (m metadataClientSessionCache) Put(key string, session *tls.ClientSessionSt
var _ = Describe("0-RTT", func() {
rtt := scaleDuration(5 * time.Millisecond)
runCountingProxy := func(serverPort int) (*quicproxy.QuicProxy, *uint32) {
var num0RTTPackets uint32 // to be used as an atomic
runCountingProxy := func(serverPort int) (*quicproxy.QuicProxy, *atomic.Uint32) {
var num0RTTPackets atomic.Uint32
proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
RemoteAddr: fmt.Sprintf("localhost:%d", serverPort),
DelayPacket: func(_ quicproxy.Direction, data []byte) time.Duration {
@ -69,7 +66,7 @@ var _ = Describe("0-RTT", func() {
hdr, _, rest, err := wire.ParsePacket(data)
Expect(err).ToNot(HaveOccurred())
if hdr.Type == protocol.PacketType0RTT {
atomic.AddUint32(&num0RTTPackets, 1)
num0RTTPackets.Add(1)
break
}
data = rest
@ -179,6 +176,7 @@ var _ = Describe("0-RTT", func() {
Conn: udpConn,
ConnectionIDLength: connIDLen,
}
addTracer(tr)
defer tr.Close()
conn, err = tr.DialEarly(
context.Background(),
@ -290,7 +288,7 @@ var _ = Describe("0-RTT", func() {
Expect(numNewConnIDs).ToNot(BeZero())
}
num0RTT := atomic.LoadUint32(num0RTTPackets)
num0RTT := num0RTTPackets.Load()
fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
Expect(num0RTT).ToNot(BeZero())
zeroRTTPackets := get0RTTPackets(counter.getRcvdLongHeaderPackets())
@ -383,10 +381,7 @@ var _ = Describe("0-RTT", func() {
})
It("transfers 0-RTT data, when 0-RTT packets are lost", func() {
var (
num0RTTPackets uint32 // to be used as an atomic
num0RTTDropped uint32
)
var num0RTTPackets, numDropped atomic.Uint32
tlsConf := getTLSConfig()
clientConf := getTLSClientConfig()
@ -405,17 +400,8 @@ var _ = Describe("0-RTT", func() {
defer ln.Close()
proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
RemoteAddr: fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port),
DelayPacket: func(_ quicproxy.Direction, data []byte) time.Duration {
if wire.IsLongHeaderPacket(data[0]) {
hdr, _, _, err := wire.ParsePacket(data)
Expect(err).ToNot(HaveOccurred())
if hdr.Type == protocol.PacketType0RTT {
atomic.AddUint32(&num0RTTPackets, 1)
}
}
return rtt / 2
},
RemoteAddr: fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port),
DelayPacket: func(_ quicproxy.Direction, data []byte) time.Duration { return rtt / 2 },
DropPacket: func(_ quicproxy.Direction, data []byte) bool {
if !wire.IsLongHeaderPacket(data[0]) {
return false
@ -423,10 +409,11 @@ var _ = Describe("0-RTT", func() {
hdr, _, _, err := wire.ParsePacket(data)
Expect(err).ToNot(HaveOccurred())
if hdr.Type == protocol.PacketType0RTT {
count := num0RTTPackets.Add(1)
// drop 25% of the 0-RTT packets
drop := mrand.Intn(4) == 0
drop := count%4 == 0
if drop {
atomic.AddUint32(&num0RTTDropped, 1)
numDropped.Add(1)
}
return drop
}
@ -438,10 +425,9 @@ var _ = Describe("0-RTT", func() {
transfer0RTTData(ln, proxy.LocalPort(), protocol.DefaultConnectionIDLength, clientConf, nil, PRData)
num0RTT := atomic.LoadUint32(&num0RTTPackets)
numDropped := atomic.LoadUint32(&num0RTTDropped)
fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets. Dropped %d of those.", num0RTT, numDropped)
Expect(numDropped).ToNot(BeZero())
num0RTT := num0RTTPackets.Load()
fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets. Dropped %d of those.", num0RTT, numDropped.Load())
Expect(numDropped.Load()).ToNot(BeZero())
Expect(num0RTT).ToNot(BeZero())
Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).ToNot(BeEmpty())
})
@ -470,14 +456,20 @@ var _ = Describe("0-RTT", func() {
}
counter, tracer := newPacketTracer()
ln, err := quic.ListenAddrEarly(
"localhost:0",
laddr, err := net.ResolveUDPAddr("udp", "localhost:0")
Expect(err).ToNot(HaveOccurred())
udpConn, err := net.ListenUDP("udp", laddr)
Expect(err).ToNot(HaveOccurred())
defer udpConn.Close()
tr := &quic.Transport{
Conn: udpConn,
VerifySourceAddress: func(net.Addr) bool { return true },
}
addTracer(tr)
defer tr.Close()
ln, err := tr.ListenEarly(
tlsConf,
getQuicConfig(&quic.Config{
RequireAddressValidation: func(net.Addr) bool { return true },
Allow0RTT: true,
Tracer: newTracer(tracer),
}),
getQuicConfig(&quic.Config{Allow0RTT: true, Tracer: newTracer(tracer)}),
)
Expect(err).ToNot(HaveOccurred())
defer ln.Close()
@ -524,6 +516,39 @@ var _ = Describe("0-RTT", func() {
Expect(zeroRTTPackets[0]).To(BeNumerically(">=", protocol.PacketNumber(5)))
})
It("doesn't use 0-RTT when Dial is used for the resumed connection", func() {
tlsConf := getTLSConfig()
clientConf := getTLSClientConfig()
dialAndReceiveSessionTicket(tlsConf, getQuicConfig(nil), clientConf)
ln, err := quic.ListenAddrEarly(
"localhost:0",
tlsConf,
getQuicConfig(&quic.Config{Allow0RTT: true}),
)
Expect(err).ToNot(HaveOccurred())
defer ln.Close()
proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port)
defer proxy.Close()
conn, err := quic.DialAddr(
context.Background(),
fmt.Sprintf("localhost:%d", proxy.LocalPort()),
clientConf,
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
defer conn.CloseWithError(0, "")
Expect(conn.ConnectionState().TLS.DidResume).To(BeTrue())
Expect(conn.ConnectionState().Used0RTT).To(BeFalse())
Expect(num0RTTPackets.Load()).To(BeZero())
serverConn, err := ln.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())
Expect(serverConn.ConnectionState().TLS.DidResume).To(BeTrue())
Expect(serverConn.ConnectionState().Used0RTT).To(BeFalse())
})
It("doesn't reject 0-RTT when the server's transport stream limit increased", func() {
const maxStreams = 1
tlsConf := getTLSConfig()
@ -595,7 +620,7 @@ var _ = Describe("0-RTT", func() {
check0RTTRejected(ln, proxy.LocalPort(), clientConf)
// The client should send 0-RTT packets, but the server doesn't process them.
num0RTT := atomic.LoadUint32(num0RTTPackets)
num0RTT := num0RTTPackets.Load()
fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
Expect(num0RTT).ToNot(BeZero())
Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty())
@ -628,7 +653,7 @@ var _ = Describe("0-RTT", func() {
check0RTTRejected(ln, proxy.LocalPort(), clientConf)
// The client should send 0-RTT packets, but the server doesn't process them.
num0RTT := atomic.LoadUint32(num0RTTPackets)
num0RTT := num0RTTPackets.Load()
fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
Expect(num0RTT).ToNot(BeZero())
Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty())
@ -657,12 +682,55 @@ var _ = Describe("0-RTT", func() {
check0RTTRejected(ln, proxy.LocalPort(), clientConf)
// The client should send 0-RTT packets, but the server doesn't process them.
num0RTT := atomic.LoadUint32(num0RTTPackets)
num0RTT := num0RTTPackets.Load()
fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
Expect(num0RTT).ToNot(BeZero())
Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty())
})
It("doesn't use 0-RTT, if the server didn't enable it", func() {
server, err := quic.ListenAddr("localhost:0", getTLSConfig(), getQuicConfig(nil))
Expect(err).ToNot(HaveOccurred())
defer server.Close()
gets := make(chan string, 100)
puts := make(chan string, 100)
cache := newClientSessionCache(tls.NewLRUClientSessionCache(10), gets, puts)
tlsConf := getTLSClientConfig()
tlsConf.ClientSessionCache = cache
conn1, err := quic.DialAddr(
context.Background(),
fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
tlsConf,
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
defer conn1.CloseWithError(0, "")
var sessionKey string
Eventually(puts).Should(Receive(&sessionKey))
Expect(conn1.ConnectionState().TLS.DidResume).To(BeFalse())
serverConn, err := server.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())
Expect(serverConn.ConnectionState().TLS.DidResume).To(BeFalse())
conn2, err := quic.DialAddrEarly(
context.Background(),
fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
tlsConf,
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
Expect(gets).To(Receive(Equal(sessionKey)))
Expect(conn2.ConnectionState().TLS.DidResume).To(BeTrue())
serverConn, err = server.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())
Expect(serverConn.ConnectionState().TLS.DidResume).To(BeTrue())
Expect(serverConn.ConnectionState().Used0RTT).To(BeFalse())
conn2.CloseWithError(0, "")
})
DescribeTable("flow control limits",
func(addFlowControlLimit func(*quic.Config, uint64)) {
counter, tracer := newPacketTracer()
@ -813,7 +881,7 @@ var _ = Describe("0-RTT", func() {
Expect(conn.CloseWithError(0, "")).To(Succeed())
// The client should send 0-RTT packets, but the server doesn't process them.
num0RTT := atomic.LoadUint32(num0RTTPackets)
num0RTT := num0RTTPackets.Load()
fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
Expect(num0RTT).ToNot(BeZero())
Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty())
@ -961,7 +1029,7 @@ var _ = Describe("0-RTT", func() {
defer close(received)
conn, err := ln.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())
receivedMessage, err = conn.ReceiveMessage(context.Background())
receivedMessage, err = conn.ReceiveDatagram(context.Background())
Expect(err).ToNot(HaveOccurred())
Expect(conn.ConnectionState().Used0RTT).To(BeTrue())
}()
@ -975,14 +1043,14 @@ var _ = Describe("0-RTT", func() {
)
Expect(err).ToNot(HaveOccurred())
Expect(conn.ConnectionState().SupportsDatagrams).To(BeTrue())
Expect(conn.SendMessage(sentMessage)).To(Succeed())
Expect(conn.SendDatagram(sentMessage)).To(Succeed())
<-conn.HandshakeComplete()
<-received
Expect(conn.ConnectionState().Used0RTT).To(BeTrue())
Expect(conn.CloseWithError(0, "")).To(Succeed())
Expect(receivedMessage).To(Equal(sentMessage))
num0RTT := atomic.LoadUint32(num0RTTPackets)
num0RTT := num0RTTPackets.Load()
fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
Expect(num0RTT).ToNot(BeZero())
zeroRTTPackets := get0RTTPackets(counter.getRcvdLongHeaderPackets())
@ -1017,7 +1085,7 @@ var _ = Describe("0-RTT", func() {
defer GinkgoRecover()
conn, err := ln.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())
_, err = conn.ReceiveMessage(context.Background())
_, err = conn.ReceiveDatagram(context.Background())
Expect(err.Error()).To(Equal("datagram support disabled"))
<-conn.HandshakeComplete()
Expect(conn.ConnectionState().Used0RTT).To(BeFalse())
@ -1033,13 +1101,13 @@ var _ = Describe("0-RTT", func() {
Expect(err).ToNot(HaveOccurred())
// the client can temporarily send datagrams but the server doesn't process them.
Expect(conn.ConnectionState().SupportsDatagrams).To(BeTrue())
Expect(conn.SendMessage(make([]byte, 100))).To(Succeed())
Expect(conn.SendDatagram(make([]byte, 100))).To(Succeed())
<-conn.HandshakeComplete()
Expect(conn.ConnectionState().SupportsDatagrams).To(BeFalse())
Expect(conn.ConnectionState().Used0RTT).To(BeFalse())
Expect(conn.CloseWithError(0, "")).To(Succeed())
num0RTT := atomic.LoadUint32(num0RTTPackets)
num0RTT := num0RTTPackets.Load()
fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT)
Expect(num0RTT).ToNot(BeZero())
Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty())

View file

@ -141,7 +141,7 @@ var _ = Describe("QUIC Proxy", func() {
Context("Proxy tests", func() {
var (
serverConn *net.UDPConn
serverNumPacketsSent int32
serverNumPacketsSent atomic.Int32
serverReceivedPackets chan packetData
clientConn *net.UDPConn
proxy *QuicProxy
@ -159,9 +159,9 @@ var _ = Describe("QUIC Proxy", func() {
BeforeEach(func() {
stoppedReading = make(chan struct{})
serverReceivedPackets = make(chan packetData, 100)
atomic.StoreInt32(&serverNumPacketsSent, 0)
serverNumPacketsSent.Store(0)
// setup a dump UDP server
// set up a dump UDP server
// in production this would be a QUIC server
raddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
Expect(err).ToNot(HaveOccurred())
@ -181,7 +181,7 @@ var _ = Describe("QUIC Proxy", func() {
data := buf[0:n]
serverReceivedPackets <- packetData(data)
// echo the packet
atomic.AddInt32(&serverNumPacketsSent, 1)
serverNumPacketsSent.Add(1)
serverConn.WriteToUDP(data, addr)
}
}()
@ -236,7 +236,7 @@ var _ = Describe("QUIC Proxy", func() {
}()
Eventually(serverReceivedPackets).Should(HaveLen(2))
Expect(atomic.LoadInt32(&serverNumPacketsSent)).To(BeEquivalentTo(2))
Expect(serverNumPacketsSent.Load()).To(BeEquivalentTo(2))
Eventually(clientReceivedPackets).Should(HaveLen(2))
Expect(string(<-clientReceivedPackets)).To(ContainSubstring("foobar"))
Expect(string(<-clientReceivedPackets)).To(ContainSubstring("decafbad"))
@ -245,14 +245,14 @@ var _ = Describe("QUIC Proxy", func() {
Context("Drop Callbacks", func() {
It("drops incoming packets", func() {
var counter int32
var counter atomic.Int32
opts := &Opts{
RemoteAddr: serverConn.LocalAddr().String(),
DropPacket: func(d Direction, _ []byte) bool {
if d != DirectionIncoming {
return false
}
return atomic.AddInt32(&counter, 1)%2 == 1
return counter.Add(1)%2 == 1
},
}
startProxy(opts)
@ -267,14 +267,14 @@ var _ = Describe("QUIC Proxy", func() {
It("drops outgoing packets", func() {
const numPackets = 6
var counter int32
var counter atomic.Int32
opts := &Opts{
RemoteAddr: serverConn.LocalAddr().String(),
DropPacket: func(d Direction, _ []byte) bool {
if d != DirectionOutgoing {
return false
}
return atomic.AddInt32(&counter, 1)%2 == 1
return counter.Add(1)%2 == 1
},
}
startProxy(opts)
@ -315,7 +315,7 @@ var _ = Describe("QUIC Proxy", func() {
}
It("delays incoming packets", func() {
var counter int32
var counter atomic.Int32
opts := &Opts{
RemoteAddr: serverConn.LocalAddr().String(),
// delay packet 1 by 200 ms
@ -325,7 +325,7 @@ var _ = Describe("QUIC Proxy", func() {
if d == DirectionOutgoing {
return 0
}
p := atomic.AddInt32(&counter, 1)
p := counter.Add(1)
return time.Duration(p) * delay
},
}
@ -349,7 +349,7 @@ var _ = Describe("QUIC Proxy", func() {
})
It("handles reordered packets", func() {
var counter int32
var counter atomic.Int32
opts := &Opts{
RemoteAddr: serverConn.LocalAddr().String(),
// delay packet 1 by 600 ms
@ -359,7 +359,7 @@ var _ = Describe("QUIC Proxy", func() {
if d == DirectionOutgoing {
return 0
}
p := atomic.AddInt32(&counter, 1)
p := counter.Add(1)
return 600*time.Millisecond - time.Duration(p-1)*delay
},
}
@ -407,7 +407,7 @@ var _ = Describe("QUIC Proxy", func() {
It("delays outgoing packets", func() {
const numPackets = 3
var counter int32
var counter atomic.Int32
opts := &Opts{
RemoteAddr: serverConn.LocalAddr().String(),
// delay packet 1 by 200 ms
@ -417,7 +417,7 @@ var _ = Describe("QUIC Proxy", func() {
if d == DirectionIncoming {
return 0
}
p := atomic.AddInt32(&counter, 1)
p := counter.Add(1)
return time.Duration(p) * delay
},
}

View file

@ -7,6 +7,7 @@ import (
"io"
"log"
"os"
"time"
quic "github.com/refraction-networking/uquic"
"github.com/refraction-networking/uquic/internal/utils"
@ -14,13 +15,21 @@ import (
"github.com/refraction-networking/uquic/qlog"
)
func NewQlogger(logger io.Writer) func(context.Context, logging.Perspective, quic.ConnectionID) *logging.ConnectionTracer {
func QlogTracer(logger io.Writer) *logging.Tracer {
filename := fmt.Sprintf("log_%s_transport.qlog", time.Now().Format("2006-01-02T15:04:05"))
fmt.Fprintf(logger, "Creating %s.\n", filename)
f, err := os.Create(filename)
if err != nil {
log.Fatalf("failed to create qlog file: %s", err)
return nil
}
bw := bufio.NewWriter(f)
return qlog.NewTracer(utils.NewBufferedWriteCloser(bw, f))
}
func NewQlogConnectionTracer(logger io.Writer) func(context.Context, logging.Perspective, quic.ConnectionID) *logging.ConnectionTracer {
return func(_ context.Context, p logging.Perspective, connID quic.ConnectionID) *logging.ConnectionTracer {
role := "server"
if p == logging.PerspectiveClient {
role = "client"
}
filename := fmt.Sprintf("log_%x_%s.qlog", connID.Bytes(), role)
filename := fmt.Sprintf("log_%s_%s.qlog", connID, p.String())
fmt.Fprintf(logger, "Creating %s.\n", filename)
f, err := os.Create(filename)
if err != nil {

View file

@ -19,7 +19,7 @@ import (
)
type versioner interface {
GetVersion() protocol.VersionNumber
GetVersion() protocol.Version
}
type result struct {
@ -69,11 +69,11 @@ var _ = Describe("Handshake tests", func() {
}
}
var supportedVersions []protocol.VersionNumber
var supportedVersions []protocol.Version
BeforeEach(func() {
supportedVersions = append([]quic.VersionNumber{}, protocol.SupportedVersions...)
protocol.SupportedVersions = append(protocol.SupportedVersions, []protocol.VersionNumber{7, 8, 9, 10}...)
supportedVersions = append([]quic.Version{}, protocol.SupportedVersions...)
protocol.SupportedVersions = append(protocol.SupportedVersions, []protocol.Version{7, 8, 9, 10}...)
})
AfterEach(func() {
@ -86,7 +86,7 @@ var _ = Describe("Handshake tests", func() {
// the server doesn't support the highest supported version, which is the first one the client will try
// but it supports a bunch of versions that the client doesn't speak
serverConfig := &quic.Config{}
serverConfig.Versions = []protocol.VersionNumber{7, 8, protocol.SupportedVersions[0], 9}
serverConfig.Versions = []protocol.Version{7, 8, protocol.SupportedVersions[0], 9}
serverResult, serverTracer := newVersionNegotiationTracer()
serverConfig.Tracer = func(context.Context, logging.Perspective, quic.ConnectionID) *logging.ConnectionTracer {
return serverTracer
@ -126,7 +126,7 @@ var _ = Describe("Handshake tests", func() {
}
server, cl := startServer(getTLSConfig(), serverConfig)
defer cl()
clientVersions := []protocol.VersionNumber{7, 8, 9, protocol.SupportedVersions[0], 10}
clientVersions := []protocol.Version{7, 8, 9, protocol.SupportedVersions[0], 10}
clientResult, clientTracer := newVersionNegotiationTracer()
conn, err := quic.DialAddr(
context.Background(),
@ -170,7 +170,7 @@ var _ = Describe("Handshake tests", func() {
Expect(err).ToNot(HaveOccurred())
defer ln.Close()
clientVersions := []protocol.VersionNumber{7, 8, 9, protocol.SupportedVersions[0], 10}
clientVersions := []protocol.Version{7, 8, 9, protocol.SupportedVersions[0], 10}
clientResult, clientTracer := newVersionNegotiationTracer()
_, err = quic.DialAddr(
context.Background(),

View file

@ -65,7 +65,7 @@ func maybeAddQLOGTracer(c *quic.Config) *quic.Config {
if !enableQlog {
return c
}
qlogger := tools.NewQlogger(GinkgoWriter)
qlogger := tools.NewQlogConnectionTracer(GinkgoWriter)
if c.Tracer == nil {
c.Tracer = qlogger
} else if qlogger != nil {