From 823fa92f99c4bf1093bc922d4f47ff2f94314781 Mon Sep 17 00:00:00 2001
From: Andrey Meshkov <am@adguard.com>
Date: Fri, 16 Sep 2022 18:39:28 +0300
Subject: [PATCH] Pull request: Added DoH3 support, added TLS resumption

Merge in DNS/dnsproxy from doh3 to master

Squashed commit of the following:

commit 93dc50875caf2df86ce08f22f5fb74e33b7b5ac0
Author: Andrey Meshkov <am@adguard.com>
Date:   Fri Sep 16 18:33:38 2022 +0300

    fix review comments

commit d19fd61eb69f31c94a9374396cbbefeb566a2163
Author: Andrey Meshkov <am@adguard.com>
Date:   Fri Sep 16 17:28:21 2022 +0300

    upstream: added comments, minor fixes

commit 9e4bf71275e9d1d3bc1cd72e27812548e8158402
Author: Andrey Meshkov <am@adguard.com>
Date:   Fri Sep 16 16:59:57 2022 +0300

    upstream: added DoH3 support, added TLS resumption
    The changes are pretty considerable in this PR.

    First of all, DoH3 support has been added to dnsOverHTTPS. I haven't added
    a new type of upstream for that, but added it to the already existing one.
    Configuring supported HTTP versions is possible via upstream.Options. When all
    versions are enabled, it will "probe" both TLS and QUIC and choose the one that
    was faster (just like it's done in Chrome).

    Command-line interface now supports a new argument "http3" that is supposed to
    enable HTTP/3 globally. At this point it will only enable it for upstreams, but
    in the future it will also enable it for the DoH server.

    One more important change here is the introduction of TLS sessions cache. It
    appears that we weren't benefiting from TLS session resumption mechanism at all,
    thank god this is finally fixed.

    Finally, AddressToUpstream now supports "h3://" scheme for those who want to try
    DoH3 for a particular upstream without enabling it globally. The reasoning for
    implementing this custom scheme is the following: currently, only a small share
    of public resolvers fully support DoH3. Users may not want to spend time
    "probing" every upstream for H3.

commit 8c76e435860699a2d5815fc702b7a7e928eba3ed
Merge: 1145771 1dd831b
Author: Andrey Meshkov <am@adguard.com>
Date:   Fri Sep 16 12:05:34 2022 +0300

    Merge branch 'justus-forks-doq-0rtt' into doh3

commit 1145771f7621be5778cf14b47ccfb4aa20d07c81
Author: Andrey Meshkov <am@adguard.com>
Date:   Fri Sep 16 11:45:42 2022 +0300

    upstream: initial attempt to add a DOH3 upstream
---
 .github/workflows/lint.yaml                   |   2 +-
 README.md                                     |  30 +-
 go.mod                                        |   1 +
 go.sum                                        |   6 +
 main.go                                       |  16 ++
 proxy/errors.go                               |  10 +-
 proxy/server_https.go                         |  18 +-
 proxy/upstreams.go                            |   8 +-
 proxyutil/udp_unix.go                         |   5 +-
 upstream/bootstrap.go                         | 155 ++++++----
 upstream/upstream.go                          |  79 ++++--
 upstream/upstream_dnscrypt.go                 |   7 +-
 upstream/upstream_doh.go                      | 243 +++++++++++++++-
 upstream/upstream_doh_test.go                 | 264 ++++++++++++++++++
 upstream/upstream_dot.go                      |   7 +-
 upstream/upstream_plain.go                    |   8 +-
 upstream/upstream_pool.go                     |  57 ++--
 upstream/upstream_pool_test.go                |  33 ++-
 upstream/upstream_quic.go                     |  80 +++---
 upstream/upstream_quic_test.go                |  23 +-
 upstream/upstream_test.go                     | 183 +++++++++---
 .../lucas-clemente/quic-go/http3/body.go      | Bin 0 -> 2970 bytes
 .../lucas-clemente/quic-go/http3/client.go    | Bin 0 -> 12686 bytes
 .../quic-go/http3/error_codes.go              | Bin 0 -> 2089 bytes
 .../lucas-clemente/quic-go/http3/frames.go    | Bin 0 -> 3740 bytes
 .../quic-go/http3/gzip_reader.go              | Bin 0 -> 798 bytes
 .../quic-go/http3/http_stream.go              | Bin 0 -> 1706 bytes
 .../lucas-clemente/quic-go/http3/request.go   | Bin 0 -> 2599 bytes
 .../quic-go/http3/request_writer.go           | Bin 0 -> 8116 bytes
 .../quic-go/http3/response_writer.go          | Bin 0 -> 2712 bytes
 .../lucas-clemente/quic-go/http3/roundtrip.go | Bin 0 -> 7166 bytes
 .../lucas-clemente/quic-go/http3/server.go    | Bin 0 -> 22266 bytes
 .../marten-seemann/qpack/.codecov.yml         | Bin 0 -> 103 bytes
 .../marten-seemann/qpack/.gitignore           | Bin 0 -> 110 bytes
 .../marten-seemann/qpack/.gitmodules          | Bin 0 -> 126 bytes
 .../marten-seemann/qpack/.golangci.yml        | Bin 0 -> 447 bytes
 .../marten-seemann/qpack/LICENSE.md           | Bin 0 -> 1054 bytes
 .../github.com/marten-seemann/qpack/README.md | Bin 0 -> 1203 bytes
 .../marten-seemann/qpack/decoder.go           | Bin 0 -> 6190 bytes
 .../marten-seemann/qpack/encoder.go           | Bin 0 -> 2616 bytes
 .../marten-seemann/qpack/header_field.go      | Bin 0 -> 477 bytes
 .../marten-seemann/qpack/static_table.go      | Bin 0 -> 9113 bytes
 .../github.com/marten-seemann/qpack/varint.go | Bin 0 -> 1589 bytes
 vendor/modules.txt                            | Bin 6446 -> 6580 bytes
 44 files changed, 999 insertions(+), 236 deletions(-)
 create mode 100644 upstream/upstream_doh_test.go
 create mode 100644 vendor/github.com/lucas-clemente/quic-go/http3/body.go
 create mode 100644 vendor/github.com/lucas-clemente/quic-go/http3/client.go
 create mode 100644 vendor/github.com/lucas-clemente/quic-go/http3/error_codes.go
 create mode 100644 vendor/github.com/lucas-clemente/quic-go/http3/frames.go
 create mode 100644 vendor/github.com/lucas-clemente/quic-go/http3/gzip_reader.go
 create mode 100644 vendor/github.com/lucas-clemente/quic-go/http3/http_stream.go
 create mode 100644 vendor/github.com/lucas-clemente/quic-go/http3/request.go
 create mode 100644 vendor/github.com/lucas-clemente/quic-go/http3/request_writer.go
 create mode 100644 vendor/github.com/lucas-clemente/quic-go/http3/response_writer.go
 create mode 100644 vendor/github.com/lucas-clemente/quic-go/http3/roundtrip.go
 create mode 100644 vendor/github.com/lucas-clemente/quic-go/http3/server.go
 create mode 100644 vendor/github.com/marten-seemann/qpack/.codecov.yml
 create mode 100644 vendor/github.com/marten-seemann/qpack/.gitignore
 create mode 100644 vendor/github.com/marten-seemann/qpack/.gitmodules
 create mode 100644 vendor/github.com/marten-seemann/qpack/.golangci.yml
 create mode 100644 vendor/github.com/marten-seemann/qpack/LICENSE.md
 create mode 100644 vendor/github.com/marten-seemann/qpack/README.md
 create mode 100644 vendor/github.com/marten-seemann/qpack/decoder.go
 create mode 100644 vendor/github.com/marten-seemann/qpack/encoder.go
 create mode 100644 vendor/github.com/marten-seemann/qpack/header_field.go
 create mode 100644 vendor/github.com/marten-seemann/qpack/static_table.go
 create mode 100644 vendor/github.com/marten-seemann/qpack/varint.go

diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml
index ccc01397..12ae895a 100644
--- a/.github/workflows/lint.yaml
+++ b/.github/workflows/lint.yaml
@@ -29,7 +29,7 @@ jobs:
         with:
           # This field is required.  Dont set the patch version to always use
           # the latest patch version.
-          version: v1.45.1
+          version: v1.48.0
   notify:
     needs:
       - golangci
diff --git a/README.md b/README.md
index 50c963ee..ff36c26c 100644
--- a/README.md
+++ b/README.md
@@ -37,8 +37,8 @@ Usage:
   dnsproxy [OPTIONS]
 
 Application Options:
-      --config-path=      yaml configuration file. Minimal working configuration in config.yaml.dist. Options passed
-                          through command line will override the ones from this file.
+      --config-path=      yaml configuration file. Minimal working configuration in config.yaml.dist.
+                          Options passed through command line will override the ones from this file.
   -v, --verbose           Verbose output (optional)
   -o, --output=           Path to the log file. If not set, write to stdout.
   -l, --listen=           Listening addresses
@@ -52,10 +52,10 @@ Application Options:
       --tls-min-version=  Minimum TLS version, for example 1.0
       --tls-max-version=  Maximum TLS version, for example 1.3
       --insecure          Disable secure TLS certificate validation
-  -g, --dnscrypt-config=  Path to a file with DNSCrypt configuration. You can generate one using
-                          https://github.com/ameshkov/dnscrypt
-  -u, --upstream=         An upstream to be used (can be specified multiple times). You can also specify path to a file
-                          with the list of servers
+  -g, --dnscrypt-config=  Path to a file with DNSCrypt configuration. You can generate one using https://github.com/ameshkov/dnscrypt
+      --http3             Enable HTTP/3 support
+  -u, --upstream=         An upstream to be used (can be specified multiple times).
+                          You can also specify path to a file with the list of servers
   -b, --bootstrap=        Bootstrap DNS for DoH and DoT, can be specified multiple times (default: 8.8.8.8:53)
   -f, --fallback=         Fallback resolvers to use when regular ones are unavailable, can be specified multiple times.
                           You can also specify path to a file with the list of servers
@@ -63,8 +63,8 @@ Application Options:
       --fastest-addr      Respond to A or AAAA requests only with the fastest IP address
       --cache             If specified, DNS cache is enabled
       --cache-size=       Cache size (in bytes). Default: 64k
-      --cache-min-ttl=    Minimum TTL value for DNS entries, in seconds. Capped at 3600. Artificially extending TTLs
-                          should only be done with careful consideration.
+      --cache-min-ttl=    Minimum TTL value for DNS entries, in seconds. Capped at 3600.
+                          Artificially extending TTLs should only be done with careful consideration.
       --cache-max-ttl=    Maximum TTL value for DNS entries, in seconds.
       --cache-optimistic  If specified, optimistic DNS cache is enabled
   -r, --ratelimit=        Ratelimit (requests per second)
@@ -75,8 +75,8 @@ Application Options:
       --dns64-prefix=     If specified, this is the DNS64 prefix dnsproxy will be using when it works as a DNS64 server.
                           If not specified, dnsproxy uses the 'Well-Known Prefix' 64:ff9b::
       --ipv6-disabled     If specified, all AAAA requests will be replied with NoError RCode and empty answer
-      --bogus-nxdomain=   Transform the responses containing at least a single IP that matches specified addresses and
-                          CIDRs into NXDOMAIN.  Can be specified multiple times.
+      --bogus-nxdomain=   Transform the responses containing at least a single IP that matches specified addresses
+                          and CIDRs into NXDOMAIN.  Can be specified multiple times.
       --udp-buf-size=     Set the size of the UDP buffer in bytes. A value <= 0 will use the system default.
       --max-go-routines=  Set the maximum number of go routines. A value <= 0 will not not set a maximum.
       --version           Prints the program version
@@ -144,6 +144,16 @@ DNS-over-QUIC upstream:
 ./dnsproxy -u quic://dns.adguard.com
 ```
 
+DNS-over-HTTPS upstream with enabled HTTP/3 support (chooses it if it's faster):
+```shell
+./dnsproxy -u https://dns.google/dns-query --http3
+```
+
+DNS-over-HTTPS upstream with forced HTTP/3 (no fallback to other protocol):
+```shell
+./dnsproxy -u h3://dns.google/dns-query
+```
+
 DNSCrypt upstream ([DNS Stamp](https://dnscrypt.info/stamps) of AdGuard DNS):
 ```shell
 ./dnsproxy -u sdns://AQIAAAAAAAAAFDE3Ni4xMDMuMTMwLjEzMDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20
diff --git a/go.mod b/go.mod
index a011f4cb..52232f05 100644
--- a/go.mod
+++ b/go.mod
@@ -24,6 +24,7 @@ require (
 	github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
 	github.com/golang/mock v1.6.0 // indirect
 	github.com/kr/text v0.2.0 // indirect
+	github.com/marten-seemann/qpack v0.2.1 // indirect
 	github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect
 	github.com/marten-seemann/qtls-go1-19 v0.1.0 // indirect
 	github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
diff --git a/go.sum b/go.sum
index be7c9b39..484a8304 100644
--- a/go.sum
+++ b/go.sum
@@ -42,6 +42,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/lucas-clemente/quic-go v0.29.0 h1:Vw0mGTfmWqGzh4jx/kMymsIkFK6rErFVmg+t9RLrnZE=
 github.com/lucas-clemente/quic-go v0.29.0/go.mod h1:CTcNfLYJS2UuRNB+zcNlgvkjBhxX6Hm3WUxxAQx2mgE=
+github.com/marten-seemann/qpack v0.2.1 h1:jvTsT/HpCn2UZJdP+UUB53FfUUgeOyG5K1ns0OJOGVs=
+github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
 github.com/marten-seemann/qtls-go1-18 v0.1.2 h1:JH6jmzbduz0ITVQ7ShevK10Av5+jBEKAHMntXmIV7kM=
 github.com/marten-seemann/qtls-go1-18 v0.1.2/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
 github.com/marten-seemann/qtls-go1-19 v0.1.0 h1:rLFKD/9mp/uq1SYGYuVZhm83wkmU95pK5df3GufyYYU=
@@ -55,6 +57,7 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
 github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
+github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
 github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
 github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
@@ -87,6 +90,7 @@ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73r
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@@ -104,6 +108,7 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w
 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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-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=
@@ -117,6 +122,7 @@ golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVq
 golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
diff --git a/main.go b/main.go
index ecc3bdd4..df92091f 100644
--- a/main.go
+++ b/main.go
@@ -80,6 +80,11 @@ type Options struct {
 	// Path to the DNSCrypt configuration file
 	DNSCryptConfigPath string `yaml:"dnscrypt-config" short:"g" long:"dnscrypt-config" description:"Path to a file with DNSCrypt configuration. You can generate one using https://github.com/ameshkov/dnscrypt"`
 
+	// HTTP3 controls whether HTTP/3 is enabled for this instance of dnsproxy.
+	// At this point it only enables it for upstreams, but in the future it will
+	// also enable it for the server.
+	HTTP3 bool `yaml:"http3" long:"http3" description:"Enable HTTP/3 support" optional:"yes" optional-value:"false"`
+
 	// Upstream DNS servers settings
 	// --
 
@@ -292,7 +297,18 @@ func createProxyConfig(options *Options) proxy.Config {
 func initUpstreams(config *proxy.Config, options *Options) {
 	// Init upstreams
 	upstreams := loadServersList(options.Upstreams)
+
+	httpVersions := upstream.DefaultHTTPVersions
+	if options.HTTP3 {
+		httpVersions = []upstream.HTTPVersion{
+			upstream.HTTPVersion3,
+			upstream.HTTPVersion2,
+			upstream.HTTPVersion11,
+		}
+	}
+
 	upsOpts := &upstream.Options{
+		HTTPVersions:       httpVersions,
 		InsecureSkipVerify: options.Insecure,
 		Bootstrap:          options.BootstrapDNS,
 		Timeout:            defaultTimeout,
diff --git a/proxy/errors.go b/proxy/errors.go
index 88089c92..241fec6b 100644
--- a/proxy/errors.go
+++ b/proxy/errors.go
@@ -12,11 +12,11 @@ import (
 // isEPIPE checks if the underlying error is EPIPE.  syscall.EPIPE exists on all
 // OSes except for Plan 9.  Validate with:
 //
-//   $ for os in $(go tool dist list | cut -d / -f 1 | sort -u)
-//   do
-//           echo -n "$os"
-//           env GOOS="$os" go doc syscall.EPIPE | grep -F -e EPIPE
-//   done
+//	$ for os in $(go tool dist list | cut -d / -f 1 | sort -u)
+//	do
+//	        echo -n "$os"
+//	        env GOOS="$os" go doc syscall.EPIPE | grep -F -e EPIPE
+//	done
 //
 // For the Plan 9 version see ./errors_plan9.go.
 func isEPIPE(err error) (ok bool) {
diff --git a/proxy/server_https.go b/proxy/server_https.go
index 9fc15536..7324fc14 100644
--- a/proxy/server_https.go
+++ b/proxy/server_https.go
@@ -56,11 +56,10 @@ func (p *Proxy) listenHTTPS(srv *http.Server, l net.Listener) {
 // ServeHTTP is the http.RequestHandler implementation that handles DoH queries
 // Here is what it returns:
 //
-//  - http.StatusBadRequest if there is no DNS request data;
-//  - http.StatusUnsupportedMediaType if request content type is not
-//    "application/dns-message";
-//  - http.StatusMethodNotAllowed if request method is not GET or POST.
-//
+//   - http.StatusBadRequest if there is no DNS request data;
+//   - http.StatusUnsupportedMediaType if request content type is not
+//     "application/dns-message";
+//   - http.StatusMethodNotAllowed if request method is not GET or POST.
 func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	log.Tracef("Incoming HTTPS request on %s", r.URL)
 
@@ -159,11 +158,10 @@ func (p *Proxy) respondHTTPS(d *DNSContext) error {
 // suitable r's header.  It returns nil if r doesn't contain any information
 // about real client's IP address.  Current headers priority is:
 //
-//   1. CF-Connecting-IP
-//   2. True-Client-IP
-//   3. X-Real-IP
-//   4. X-Forwarded-For
-//
+//  1. CF-Connecting-IP
+//  2. True-Client-IP
+//  3. X-Real-IP
+//  4. X-Forwarded-For
 func realIPFromHdrs(r *http.Request) (realIP net.IP) {
 	for _, h := range []string{
 		// Headers set by CloudFlare proxy servers.
diff --git a/proxy/upstreams.go b/proxy/upstreams.go
index a57b240f..f85255a5 100644
--- a/proxy/upstreams.go
+++ b/proxy/upstreams.go
@@ -72,13 +72,7 @@ func ParseUpstreamsConfig(upstreamConfig []string, options *upstream.Options) (*
 			dnsUpstream, ok := upstreamsIndex[u]
 			if !ok {
 				// create an upstream
-				dnsUpstream, err = upstream.AddressToUpstream(
-					u,
-					&upstream.Options{
-						Bootstrap:          options.Bootstrap,
-						Timeout:            options.Timeout,
-						InsecureSkipVerify: options.InsecureSkipVerify,
-					})
+				dnsUpstream, err = upstream.AddressToUpstream(u, options.Clone())
 
 				if err != nil {
 					err = fmt.Errorf("cannot prepare the upstream %s (%s): %s", l, options.Bootstrap, err)
diff --git a/proxyutil/udp_unix.go b/proxyutil/udp_unix.go
index b5e625c3..e9d13dab 100644
--- a/proxyutil/udp_unix.go
+++ b/proxyutil/udp_unix.go
@@ -15,9 +15,8 @@ import (
 // connection to receive an appropriate OOB data.  For both versions the flags
 // are:
 //
-//   FlagDst
-//   FlagInterface
-//
+//   - FlagDst
+//   - FlagInterface
 const (
 	ipv4Flags ipv4.ControlFlags = ipv4.FlagDst | ipv4.FlagInterface
 	ipv6Flags ipv6.ControlFlags = ipv6.FlagDst | ipv6.FlagInterface
diff --git a/upstream/bootstrap.go b/upstream/bootstrap.go
index a78d6939..a5e70edb 100755
--- a/upstream/bootstrap.go
+++ b/upstream/bootstrap.go
@@ -12,53 +12,66 @@ import (
 
 	"github.com/AdguardTeam/golibs/errors"
 	"github.com/AdguardTeam/golibs/log"
-	"golang.org/x/net/http2"
 )
 
-// NextProtoDQ is the ALPN token for DoQ. During connection establishment,
-// DNS/QUIC support is indicated by selecting the ALPN token "dq" in the
+// NextProtoDQ is the ALPN token for DoQ. During the connection establishment,
+// DNS/QUIC support is indicated by selecting the ALPN token "doq" in the
 // crypto handshake.
-// Current draft version:
-// https://datatracker.ietf.org/doc/html/draft-ietf-dprive-dnsoquic-02
-const NextProtoDQ = "doq-i02"
+// The current draft version is https://datatracker.ietf.org/doc/rfc9250/.
+const NextProtoDQ = "doq"
 
 // compatProtoDQ is a list of ALPN tokens used by a QUIC connection.
 // NextProtoDQ is the latest draft version supported by dnsproxy, but it also
 // includes previous drafts.
-var compatProtoDQ = []string{NextProtoDQ, "doq-i00", "dq", "doq"}
+var compatProtoDQ = []string{NextProtoDQ, "doq-i00", "dq", "doq-i02"}
 
-// RootCAs is the CertPool that must be used by all upstreams
-// Redefining RootCAs makes sense on iOS to overcome the 15MB memory limit of the NEPacketTunnelProvider
-// nolint
+// RootCAs is the CertPool that must be used by all upstreams. Redefining
+// RootCAs makes sense on iOS to overcome the 15MB memory limit of the
+// NEPacketTunnelProvider.
 var RootCAs *x509.CertPool
 
-// CipherSuites - custom list of TLSv1.2 ciphers
-// nolint
+// CipherSuites is a custom list of TLSv1.2 ciphers.
 var CipherSuites []uint16
 
-// TODO: refactor bootstrapper, it's overcomplicated and hard to understand what it does
+// TODO(ameshkov): refactor bootstrapper, it's overcomplicated and hard to
+// understand what it does.
 type bootstrapper struct {
-	URL            *url.URL
-	resolvers      []*Resolver // list of Resolvers to use to resolve hostname, if necessary
-	dialContext    dialHandler // specifies the dial function for creating unencrypted TCP connections.
+	// URL is the upstream server address.
+	URL *url.URL
+
+	// resolvers is a list of *net.Resolver to use to resolve the upstream
+	// hostname, if necessary.
+	resolvers []*Resolver
+
+	// dialContext is the dial function for creating unencrypted TCP
+	// connections.
+	dialContext dialHandler
+
+	// resolvedConfig is a *tls.Config that is used for encrypted DNS protocols.
 	resolvedConfig *tls.Config
-	sync.RWMutex
 
-	// stores options for AddressToUpstream func:
-	// callbacks for checking certificates, timeout,
-	// the need to verify the server certificate,
-	// the addresses of upstream servers, etc
+	// sessionsCache is necessary to achieve TLS session resumption.  We create
+	// once when the bootstrapper is created and re-use every time when we need
+	// to create a new tls.Config.
+	sessionsCache tls.ClientSessionCache
+
+	// guard protects dialContext and resolvedConfig.
+	guard sync.RWMutex
+
+	// options is the Options that were passed to the AddressToUpstream
+	// function.  It configures different upstream properties: callbacks for
+	// checking certificates, timeout, etc.
 	options *Options
 }
 
-// newBootstrapperResolved creates a new bootstrapper that already contains resolved config.
-// This can be done only in the case when we already know the resolver IP address.
-// options -- Upstream customization options
+// newBootstrapperResolved creates a new bootstrapper that already contains
+// resolved config. This can be done only in the case when we already know the
+// resolver IP address passed via options.
 func newBootstrapperResolved(upsURL *url.URL, options *Options) (*bootstrapper, error) {
 	// get a host without port
 	host, port, err := net.SplitHostPort(upsURL.Host)
 	if err != nil {
-		return nil, fmt.Errorf("bootstrapper requires port in address %s", upsURL.String())
+		return nil, fmt.Errorf("bootstrapper requires port in address %s", upsURL)
 	}
 
 	var resolverAddresses []string
@@ -70,6 +83,10 @@ func newBootstrapperResolved(upsURL *url.URL, options *Options) (*bootstrapper,
 	b := &bootstrapper{
 		URL:     upsURL,
 		options: options,
+		// Use the default capacity for the LRU cache.  It may be useful to
+		// store several caches since the user may be routed to different
+		// servers in case there's load balancing on the server-side.
+		sessionsCache: tls.NewLRUClientSessionCache(0),
 	}
 	b.dialContext = b.createDialContext(resolverAddresses)
 	b.resolvedConfig = b.createTLSConfig(host)
@@ -77,9 +94,9 @@ func newBootstrapperResolved(upsURL *url.URL, options *Options) (*bootstrapper,
 	return b, nil
 }
 
-// newBootstrapper initializes a new bootstrapper instance
-// address -- original resolver address string (i.e. tls://one.one.one.one:853)
-// options -- Upstream customization options
+// newBootstrapper initializes a new bootstrapper instance. u is the original
+// resolver address string (i.e. tls://one.one.one.one:853), options is the
+// upstream configuration options.
 func newBootstrapper(u *url.URL, options *Options) (b *bootstrapper, err error) {
 	resolvers := []*Resolver{}
 	if len(options.Bootstrap) != 0 {
@@ -103,18 +120,31 @@ func newBootstrapper(u *url.URL, options *Options) (b *bootstrapper, err error)
 		URL:       u,
 		resolvers: resolvers,
 		options:   options,
+		// Use the default capacity for the LRU cache.  It may be useful to
+		// store several caches since the user may be routed to different
+		// servers in case there's load balancing on the server-side.
+		sessionsCache: tls.NewLRUClientSessionCache(0),
 	}, nil
 }
 
-// dialHandler specifies the dial function for creating unencrypted TCP connections.
+// dialHandler describes the dial function for creating unencrypted network
+// connections to the upstream server.  Internally, this function will use the
+// supplied bootstrap DNS servers to resolve the upstream's IP address and only
+// then it will actually establish a connection.
 type dialHandler func(ctx context.Context, network, addr string) (net.Conn, error)
 
-// will get usable IP address from Address field, and caches the result
+// get is the main function of bootstrapper that does two crucial things.
+// First, it creates an instance of a dialHandler function that should be used
+// by the Upstream to establish a connection to the upstream DNS server.  This
+// dialHandler in a lazy manner resolves the DNS server IP address using the
+// bootstrap DNS servers supplied to this bootstrapper instance.  It will also
+// create an instance of *tls.Config that should be used for establishing an
+// encrypted connection for DoH/DoT/DoQ.
 func (n *bootstrapper) get() (*tls.Config, dialHandler, error) {
-	n.RLock()
+	n.guard.RLock()
 	if n.dialContext != nil && n.resolvedConfig != nil { // fast path
 		tlsConfig, dialContext := n.resolvedConfig, n.dialContext
-		n.RUnlock()
+		n.guard.RUnlock()
 		return tlsConfig.Clone(), dialContext, nil
 	}
 
@@ -123,22 +153,24 @@ func (n *bootstrapper) get() (*tls.Config, dialHandler, error) {
 	//
 
 	// get a host without port
-	addr := n.URL
-	host, port, err := net.SplitHostPort(addr.Host)
+	u := n.URL
+	host, port, err := net.SplitHostPort(u.Host)
 	if err != nil {
-		n.RUnlock()
-		return nil, nil, fmt.Errorf("bootstrapper requires port in address %s", addr.String())
+		n.guard.RUnlock()
+		return nil, nil, fmt.Errorf("bootstrapper requires port in address %s", u)
 	}
 
-	// if n.address's host is an IP, just use it right away
+	// if n.address's host is an IP, just use it right away.
 	ip := net.ParseIP(host)
 	if ip != nil {
-		n.RUnlock()
+		n.guard.RUnlock()
 
-		// Upgrade lock to protect n.resolved
 		resolverAddress := net.JoinHostPort(host, port)
-		n.Lock()
-		defer n.Unlock()
+
+		// Upgrade lock to protect n.resolvedConfig.
+		// TODO(ameshkov): rework, that's not how it should be done.
+		n.guard.Lock()
+		defer n.guard.Unlock()
 
 		n.dialContext = n.createDialContext([]string{resolverAddress})
 		n.resolvedConfig = n.createTLSConfig(host)
@@ -148,7 +180,7 @@ func (n *bootstrapper) get() (*tls.Config, dialHandler, error) {
 	// Don't lock anymore (we can launch multiple lookup requests at a time)
 	// Otherwise, it might mess with the timeout specified for the Upstream
 	// See here: https://github.com/AdguardTeam/dnsproxy/issues/15
-	n.RUnlock()
+	n.guard.RUnlock()
 
 	//
 	// if it's a hostname
@@ -182,24 +214,26 @@ func (n *bootstrapper) get() (*tls.Config, dialHandler, error) {
 		return nil, nil, fmt.Errorf("couldn't find any suitable IP address for host %s", host)
 	}
 
-	n.Lock()
-	defer n.Unlock()
+	n.guard.Lock()
+	defer n.guard.Unlock()
 
 	n.dialContext = n.createDialContext(resolved)
 	n.resolvedConfig = n.createTLSConfig(host)
 	return n.resolvedConfig, n.dialContext, nil
 }
 
-// createTLSConfig creates a client TLS config
+// createTLSConfig creates a client TLS config that will be used to establish
+// an encrypted connection for DoH/DoT/DoQ.
 func (n *bootstrapper) createTLSConfig(host string) *tls.Config {
 	tlsConfig := &tls.Config{
 		ServerName:            host,
 		RootCAs:               RootCAs,
 		CipherSuites:          CipherSuites,
+		ClientSessionCache:    n.sessionsCache,
 		MinVersion:            tls.VersionTLS12,
 		InsecureSkipVerify:    n.options.InsecureSkipVerify,
 		VerifyPeerCertificate: n.options.VerifyServerCertificate,
-		ClientSessionCache:    tls.NewLRUClientSessionCache(1),
+		VerifyConnection:      n.options.VerifyConnection,
 	}
 
 	// Depending on the URL scheme, we choose what ALPN will be advertised by
@@ -210,16 +244,26 @@ func (n *bootstrapper) createTLSConfig(host string) *tls.Config {
 		//
 		// See https://github.com/ameshkov/dnslookup/issues/19.
 	case "https":
-		tlsConfig.NextProtos = []string{http2.NextProtoTLS, "http/1.1"}
+		httpVersions := n.options.HTTPVersions
+		if httpVersions == nil {
+			httpVersions = DefaultHTTPVersions
+		}
+
+		var nextProtos []string
+		for _, v := range httpVersions {
+			nextProtos = append(nextProtos, string(v))
+		}
+
+		tlsConfig.NextProtos = nextProtos
 	case "quic":
 		tlsConfig.NextProtos = compatProtoDQ
-		tlsConfig.ClientSessionCache = tls.NewLRUClientSessionCache(10)
 	}
 
 	return tlsConfig
 }
 
-// createDialContext returns dialContext function that tries to establish connection with all given addresses one by one
+// createDialContext returns a dialHandler function that tries to establish the
+// connection to each of the provided addresses one by one.
 func (n *bootstrapper) createDialContext(addresses []string) (dialContext dialHandler) {
 	dialer := &net.Dialer{
 		Timeout: n.options.Timeout,
@@ -240,14 +284,23 @@ func (n *bootstrapper) createDialContext(addresses []string) (dialContext dialHa
 			conn, err := dialer.DialContext(ctx, network, resolverAddress)
 			elapsed := time.Since(start)
 			if err == nil {
-				log.Tracef("dialer has successfully initialized connection to %s in %s", resolverAddress, elapsed)
+				log.Tracef(
+					"dialer has successfully initialized connection to %s in %s",
+					resolverAddress,
+					elapsed,
+				)
 
 				return conn, nil
 			}
 
 			errs = append(errs, err)
 
-			log.Tracef("dialer failed to initialize connection to %s, in %s, cause: %s", resolverAddress, elapsed, err)
+			log.Tracef(
+				"dialer failed to initialize connection to %s, in %s, cause: %s",
+				resolverAddress,
+				elapsed,
+				err,
+			)
 		}
 
 		return nil, errors.List("all dialers failed", errs...)
diff --git a/upstream/upstream.go b/upstream/upstream.go
index 235fcbf9..586526d6 100644
--- a/upstream/upstream.go
+++ b/upstream/upstream.go
@@ -1,7 +1,9 @@
-// Package upstream implements DNS clients for all known DNS encryption protocols
+// Package upstream implements DNS clients for all known DNS encryption
+// protocols.
 package upstream
 
 import (
+	"crypto/tls"
 	"crypto/x509"
 	"fmt"
 	"net"
@@ -17,13 +19,17 @@ import (
 	"github.com/miekg/dns"
 )
 
-// Upstream is an interface for a DNS resolver
+// Upstream is an interface for a DNS resolver.
 type Upstream interface {
+	// Exchange sends the DNS query m to this upstream and returns the response
+	// that has been received or an error if something went wrong.
 	Exchange(m *dns.Msg) (*dns.Msg, error)
+	// Address returns the address of the upstream DNS resolver.
 	Address() string
 }
 
-// Options for AddressToUpstream func
+// Options for AddressToUpstream func.  With these options we can configure the
+// upstream properties.
 type Options struct {
 	// Bootstrap is a list of DNS servers to be used to resolve
 	// DNS-over-HTTPS/DNS-over-TLS hostnames.  Plain DNS, DNSCrypt, or
@@ -42,17 +48,55 @@ type Options struct {
 	// InsecureSkipVerify disables verifying the server's certificate.
 	InsecureSkipVerify bool
 
-	// VerifyServerCertificate used to be set to crypto/tls
-	// Config.VerifyPeerCertificate for DNS-over-HTTPS, DNS-over-QUIC,
-	// DNS-over-TLS.
+	// HTTPVersions is a list of HTTP versions that should be supported by the
+	// DNS-over-HTTPS client.  If not set, HTTP/1.1 and HTTP/2 will be used.
+	HTTPVersions []HTTPVersion
+
+	// VerifyServerCertificate is used to set the VerifyPeerCertificate property
+	// of the *tls.Config for DNS-over-HTTPS, DNS-over-QUIC, and DNS-over-TLS.
 	VerifyServerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
 
+	// VerifyConnection is used to set the VerifyConnection property
+	// of the *tls.Config for DNS-over-HTTPS, DNS-over-QUIC, and DNS-over-TLS.
+	VerifyConnection func(state tls.ConnectionState) error
+
 	// VerifyDNSCryptCertificate is the callback the DNSCrypt server certificate
 	// will be passed to.  It's called in dnsCrypt.exchangeDNSCrypt.
 	// Upstream.Exchange method returns any error caused by it.
 	VerifyDNSCryptCertificate func(cert *dnscrypt.Cert) error
 }
 
+// Clone copies o to a new struct.  Note, that this is not a deep clone.
+func (o *Options) Clone() (clone *Options) {
+	return &Options{
+		Bootstrap:                 o.Bootstrap,
+		Timeout:                   o.Timeout,
+		ServerIPAddrs:             o.ServerIPAddrs,
+		InsecureSkipVerify:        o.InsecureSkipVerify,
+		HTTPVersions:              o.HTTPVersions,
+		VerifyServerCertificate:   o.VerifyServerCertificate,
+		VerifyConnection:          o.VerifyConnection,
+		VerifyDNSCryptCertificate: o.VerifyDNSCryptCertificate,
+	}
+}
+
+// HTTPVersion is an enumeration of the HTTP versions that we support.  Values
+// that we use in this enumeration are also used as ALPN values.
+type HTTPVersion string
+
+const (
+	// HTTPVersion11 is HTTP/1.1.
+	HTTPVersion11 HTTPVersion = "http/1.1"
+	// HTTPVersion2 is HTTP/2.
+	HTTPVersion2 HTTPVersion = "h2"
+	// HTTPVersion3 is HTTP/3.
+	HTTPVersion3 HTTPVersion = "h3"
+)
+
+// DefaultHTTPVersions is the list of HTTPVersion that we use by default in
+// the DNS-over-HTTPS client.
+var DefaultHTTPVersions = []HTTPVersion{HTTPVersion11, HTTPVersion2}
+
 const (
 	// defaultPortPlain is the default port for plain DNS.
 	defaultPortPlain = 53
@@ -72,11 +116,12 @@ const (
 
 // AddressToUpstream converts addr to an Upstream instance:
 //
-//  8.8.8.8:53 or udp://dns.adguard.com for plain DNS;
-//  tcp://8.8.8.8:53 for plain DNS-over-TCP;
-//  tls://1.1.1.1 for DNS-over-TLS;
-//  https://dns.adguard.com/dns-query for DNS-over-HTTPS;
-//  sdns://... for DNS stamp, see https://dnscrypt.info/stamps-specifications.
+//   - 8.8.8.8:53 or udp://dns.adguard.com for plain DNS;
+//   - tcp://8.8.8.8:53 for plain DNS-over-TCP;
+//   - tls://1.1.1.1 for DNS-over-TLS;
+//   - https://dns.adguard.com/dns-query for DNS-over-HTTPS;
+//   - h3://dns.google for DNS-over-HTTPS that only works with HTTP/3;
+//   - sdns://... for DNS stamp, see https://dnscrypt.info/stamps-specifications.
 //
 // opts are applied to the u.  nil is a valid value for opts.
 func AddressToUpstream(addr string, opts *Options) (u Upstream, err error) {
@@ -129,6 +174,10 @@ func urlToUpstream(uu *url.URL, opts *Options) (u Upstream, err error) {
 		return newDoQ(uu, opts)
 	case "tls":
 		return newDoT(uu, opts)
+	case "h3":
+		opts.HTTPVersions = []HTTPVersion{HTTPVersion3}
+		uu.Scheme = "https"
+		return newDoH(uu, opts)
 	case "https":
 		return newDoH(uu, opts)
 	default:
@@ -190,11 +239,10 @@ func logBegin(upstreamAddress string, req *dns.Msg) {
 	qtype := ""
 	target := ""
 	if len(req.Question) != 0 {
-		qtype = dns.TypeToString[req.Question[0].Qtype]
+		qtype = dns.Type(req.Question[0].Qtype).String()
 		target = req.Question[0].Name
 	}
-	log.Debug("%s: sending request %s %s",
-		upstreamAddress, qtype, target)
+	log.Debug("%s: sending request %s %s", upstreamAddress, qtype, target)
 }
 
 // Write to log about the result of DNS request
@@ -203,6 +251,5 @@ func logFinish(upstreamAddress string, err error) {
 	if err != nil {
 		status = err.Error()
 	}
-	log.Debug("%s: response: %s",
-		upstreamAddress, status)
+	log.Debug("%s: response: %s", upstreamAddress, status)
 }
diff --git a/upstream/upstream_dnscrypt.go b/upstream/upstream_dnscrypt.go
index e3c05a83..d0a0c04d 100644
--- a/upstream/upstream_dnscrypt.go
+++ b/upstream/upstream_dnscrypt.go
@@ -13,9 +13,8 @@ import (
 	"github.com/miekg/dns"
 )
 
-//
-// DNSCrypt
-//
+// dnsCrypt is a struct that implements the Upstream interface for the DNSCrypt
+// protocol.
 type dnsCrypt struct {
 	boot       *bootstrapper
 	client     *dnscrypt.Client       // DNSCrypt client properties
@@ -27,8 +26,10 @@ type dnsCrypt struct {
 // type check
 var _ Upstream = (*dnsCrypt)(nil)
 
+// Address implements the Upstream interface for *dnsCrypt.
 func (p *dnsCrypt) Address() string { return p.boot.URL.String() }
 
+// Exchange implements the Upstream interface for *dnsCrypt.
 func (p *dnsCrypt) Exchange(m *dns.Msg) (*dns.Msg, error) {
 	reply, err := p.exchangeDNSCrypt(m)
 
diff --git a/upstream/upstream_doh.go b/upstream/upstream_doh.go
index b71e64e9..78a93afa 100644
--- a/upstream/upstream_doh.go
+++ b/upstream/upstream_doh.go
@@ -1,9 +1,12 @@
 package upstream
 
 import (
+	"context"
+	"crypto/tls"
 	"encoding/base64"
 	"fmt"
 	"io"
+	"net"
 	"net/http"
 	"net/url"
 	"os"
@@ -11,6 +14,9 @@ import (
 	"time"
 
 	"github.com/AdguardTeam/golibs/errors"
+	"github.com/AdguardTeam/golibs/log"
+	"github.com/lucas-clemente/quic-go"
+	"github.com/lucas-clemente/quic-go/http3"
 	"github.com/miekg/dns"
 	"golang.org/x/net/http2"
 )
@@ -34,7 +40,8 @@ const (
 	dohMaxIdleConns = 1
 )
 
-// dnsOverHTTPS represents DNS-over-HTTPS upstream.
+// dnsOverHTTPS is a struct that implements the Upstream interface for the
+// DNS-over-HTTPS protocol.
 type dnsOverHTTPS struct {
 	boot *bootstrapper
 
@@ -43,6 +50,10 @@ type dnsOverHTTPS struct {
 	// needed. Clients are safe for concurrent use by multiple goroutines.
 	client      *http.Client
 	clientGuard sync.Mutex
+
+	// quicConfig is the QUIC configuration that is used if HTTP/3 is enabled
+	// for this upstream.
+	quicConfig *quic.Config
 }
 
 // type check
@@ -58,11 +69,25 @@ func newDoH(uu *url.URL, opts *Options) (u Upstream, err error) {
 		return nil, fmt.Errorf("creating https bootstrapper: %w", err)
 	}
 
-	return &dnsOverHTTPS{boot: b}, nil
+	return &dnsOverHTTPS{
+		boot: b,
+
+		quicConfig: &quic.Config{
+			KeepAlivePeriod: QUICKeepAlivePeriod,
+			// You can read more on address validation here:
+			// https://datatracker.ietf.org/doc/html/rfc9000#section-8.1
+			// Setting maxOrigins to 1 and tokensPerOrigin to 10 assuming that
+			// this is more than enough for the way we use it (one connection
+			// per upstream).
+			TokenStore: quic.NewLRUTokenStore(1, 10),
+		},
+	}, nil
 }
 
+// Address implements the Upstream interface for *dnsOverHTTPS.
 func (p *dnsOverHTTPS) Address() string { return p.boot.URL.String() }
 
+// Exchange implements the Upstream interface for *dnsOverHTTPS.
 func (p *dnsOverHTTPS) Exchange(m *dns.Msg) (*dns.Msg, error) {
 	client, err := p.getClient()
 	if err != nil {
@@ -146,8 +171,8 @@ func (p *dnsOverHTTPS) getClient() (c *http.Client, err error) {
 		return p.client, nil
 	}
 
-	// Timeout can be exceeded while waiting for the lock
-	// This happens quite often on mobile devices
+	// Timeout can be exceeded while waiting for the lock. This happens quite
+	// often on mobile devices.
 	elapsed := time.Since(startTime)
 	if p.boot.options.Timeout > 0 && elapsed > p.boot.options.Timeout {
 		return nil, fmt.Errorf("timeout exceeded: %s", elapsed)
@@ -158,6 +183,10 @@ func (p *dnsOverHTTPS) getClient() (c *http.Client, err error) {
 	return p.client, err
 }
 
+// createClient creates a new *http.Client instance.  The HTTP protocol version
+// will depend on whether HTTP3 is allowed and provided by this upstream.  Note,
+// that we'll attempt to establish a QUIC connection when creating the client in
+// order to check whether HTTP3 is supported.
 func (p *dnsOverHTTPS) createClient() (*http.Client, error) {
 	transport, err := p.createTransport()
 	if err != nil {
@@ -175,14 +204,32 @@ func (p *dnsOverHTTPS) createClient() (*http.Client, error) {
 }
 
 // createTransport initializes an HTTP transport that will be used specifically
-// for this DoH resolver. This HTTP transport ensures that the HTTP requests
-// will be sent exactly to the IP address got from the bootstrap resolver.
-func (p *dnsOverHTTPS) createTransport() (*http.Transport, error) {
+// for this DoH resolver.  This HTTP transport ensures that the HTTP requests
+// will be sent exactly to the IP address got from the bootstrap resolver. Note,
+// that this function will first attempt to establish a QUIC connection (if
+// HTTP3 is enabled in the upstream options).  If this attempt is successful,
+// it returns an HTTP3 transport, otherwise it returns the H1/H2 transport.
+func (p *dnsOverHTTPS) createTransport() (t http.RoundTripper, err error) {
 	tlsConfig, dialContext, err := p.boot.get()
 	if err != nil {
 		return nil, fmt.Errorf("bootstrapping %s: %w", p.boot.URL, err)
 	}
 
+	// First, we attempt to create an HTTP3 transport.  If the probe QUIC
+	// connection is established successfully, we'll be using HTTP3 for this
+	// upstream.
+	transportH3, err := p.createTransportH3(tlsConfig, dialContext)
+	if err == nil {
+		log.Debug("using HTTP/3 for this upstream: QUIC was faster")
+		return transportH3, nil
+	}
+
+	log.Debug("using HTTP/2 for this upstream: %v", err)
+
+	if !p.supportsHTTP() {
+		return nil, errors.Error("HTTP1/1 and HTTP2 are not supported by this upstream")
+	}
+
 	transport := &http.Transport{
 		TLSClientConfig:    tlsConfig,
 		DisableCompression: true,
@@ -210,3 +257,185 @@ func (p *dnsOverHTTPS) createTransport() (*http.Transport, error) {
 
 	return transport, nil
 }
+
+// createTransportH3 tries to create an HTTP/3 transport for this upstream.
+// We should be able to fall back to H1/H2 in case if HTTP/3 is unavailable or
+// if it is too slow.  In order to do that, this method will run two probes
+// in parallel (one for TLS, the other one for QUIC) and if QUIC is faster it
+// will create the *http3.RoundTripper instance.
+func (p *dnsOverHTTPS) createTransportH3(
+	tlsConfig *tls.Config,
+	dialContext dialHandler,
+) (roundTripper *http3.RoundTripper, err error) {
+	if !p.supportsH3() {
+		return nil, errors.Error("HTTP3 support is not enabled")
+	}
+
+	addr, err := p.probeH3(tlsConfig, dialContext)
+	if err != nil {
+		return nil, err
+	}
+
+	return &http3.RoundTripper{
+		Dial: func(
+			ctx context.Context,
+			// Ignore the address and always connect to the one that we got
+			// from the bootstrapper.
+			_ string,
+			tlsCfg *tls.Config,
+			cfg *quic.Config,
+		) (c quic.EarlyConnection, err error) {
+			return quic.DialAddrEarlyContext(ctx, addr, tlsCfg, cfg)
+		},
+		DisableCompression: true,
+		TLSClientConfig:    tlsConfig,
+		QuicConfig:         p.quicConfig,
+	}, nil
+}
+
+// probeH3 runs a test to check whether QUIC is faster than TLS for this
+// upstream.  If the test is successful it will return the address that we
+// should use to establish the QUIC connections.
+func (p *dnsOverHTTPS) probeH3(
+	tlsConfig *tls.Config,
+	dialContext dialHandler,
+) (addr string, err error) {
+	// We're using bootstrapped address instead of what's passed to the function
+	// it does not create an actual connection, but it helps us determine
+	// what IP is actually reachable (when there are v4/v6 addresses).
+	rawConn, err := dialContext(context.Background(), "udp", "")
+	if err != nil {
+		return "", fmt.Errorf("failed to dial: %w", err)
+	}
+	// It's never actually used.
+	_ = rawConn.Close()
+
+	udpConn, ok := rawConn.(*net.UDPConn)
+	if !ok {
+		return "", fmt.Errorf("not a UDP connection to %s", p.Address())
+	}
+
+	addr = udpConn.RemoteAddr().String()
+
+	// Avoid spending time on probing if this upstream only supports HTTP/3.
+	if p.supportsH3() && !p.supportsHTTP() {
+		return addr, nil
+	}
+
+	// Use a new *tls.Config with empty session cache for probe connections.
+	// Surprisingly, this is really important since otherwise it invalidates
+	// the existing cache.
+	// TODO(ameshkov): figure out why the sessions cache invalidates here.
+	probeTLSCfg := tlsConfig.Clone()
+	probeTLSCfg.ClientSessionCache = nil
+
+	// Do not expose probe connections to the callbacks that are passed to
+	// the bootstrap options to avoid side-effects.
+	// TODO(ameshkov): consider exposing, somehow mark that this is a probe.
+	probeTLSCfg.VerifyPeerCertificate = nil
+	probeTLSCfg.VerifyConnection = nil
+
+	// Run probeQUIC and probeTLS in parallel and see which one is faster.
+	chQuic := make(chan error, 1)
+	chTLS := make(chan error, 1)
+	go p.probeQUIC(addr, probeTLSCfg, chQuic)
+	go p.probeTLS(dialContext, probeTLSCfg, chTLS)
+
+	select {
+	case quicErr := <-chQuic:
+		if quicErr != nil {
+			// QUIC failed, return error since HTTP3 was not preferred.
+			return "", quicErr
+		}
+
+		// Return immediately, QUIC was faster.
+		return addr, quicErr
+	case tlsErr := <-chTLS:
+		if tlsErr != nil {
+			// Return immediately, TLS failed.
+			log.Debug("probing TLS: %v", tlsErr)
+			return addr, nil
+		}
+
+		return "", errors.Error("TLS was faster than QUIC, prefer it")
+	}
+}
+
+// probeQUIC attempts to establish a QUIC connection to the specified address.
+// We run probeQUIC and probeTLS in parallel and see which one is faster.
+func (p *dnsOverHTTPS) probeQUIC(addr string, tlsConfig *tls.Config, ch chan error) {
+	startTime := time.Now()
+
+	timeout := p.boot.options.Timeout
+	if timeout == 0 {
+		timeout = dialTimeout
+	}
+	ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(timeout))
+	defer cancel()
+
+	conn, err := quic.DialAddrEarlyContext(ctx, addr, tlsConfig, p.quicConfig)
+	if err != nil {
+		ch <- fmt.Errorf("opening QUIC connection to %s: %w", p.Address(), err)
+		return
+	}
+
+	// Ignore the error since there's no way we can use it for anything useful.
+	_ = conn.CloseWithError(QUICCodeNoError, "")
+
+	ch <- nil
+
+	elapsed := time.Now().Sub(startTime)
+	log.Debug("elapsed on establishing a QUIC connection: %s", elapsed)
+}
+
+// probeTLS attempts to establish a TLS connection to the specified address. We
+// run probeQUIC and probeTLS in parallel and see which one is faster.
+func (p *dnsOverHTTPS) probeTLS(dialContext dialHandler, tlsConfig *tls.Config, ch chan error) {
+	startTime := time.Now()
+
+	conn, err := tlsDial(dialContext, "tcp", tlsConfig)
+	if err != nil {
+		ch <- fmt.Errorf("opening TLS connection: %w", err)
+		return
+	}
+
+	// Ignore the error since there's no way we can use it for anything useful.
+	_ = conn.Close()
+
+	ch <- nil
+
+	elapsed := time.Now().Sub(startTime)
+	log.Debug("elapsed on establishing a TLS connection: %s", elapsed)
+}
+
+// supportsH3 returns true if HTTP/3 is supported by this upstream.
+func (p *dnsOverHTTPS) supportsH3() (ok bool) {
+	for _, v := range p.supportedHTTPVersions() {
+		if v == HTTPVersion3 {
+			return true
+		}
+	}
+
+	return false
+}
+
+// supportsHTTP returns true if HTTP/1.1 or HTTP2 is supported by this upstream.
+func (p *dnsOverHTTPS) supportsHTTP() (ok bool) {
+	for _, v := range p.supportedHTTPVersions() {
+		if v == HTTPVersion11 || v == HTTPVersion2 {
+			return true
+		}
+	}
+
+	return false
+}
+
+// supportedHTTPVersions returns the list of supported HTTP versions.
+func (p *dnsOverHTTPS) supportedHTTPVersions() (v []HTTPVersion) {
+	v = p.boot.options.HTTPVersions
+	if v == nil {
+		v = DefaultHTTPVersions
+	}
+
+	return v
+}
diff --git a/upstream/upstream_doh_test.go b/upstream/upstream_doh_test.go
new file mode 100644
index 00000000..16a9a09f
--- /dev/null
+++ b/upstream/upstream_doh_test.go
@@ -0,0 +1,264 @@
+package upstream
+
+import (
+	"context"
+	"crypto/tls"
+	"encoding/base64"
+	"fmt"
+	"net"
+	"net/http"
+	"testing"
+	"time"
+
+	"github.com/lucas-clemente/quic-go"
+	"github.com/lucas-clemente/quic-go/http3"
+	"github.com/miekg/dns"
+	"github.com/stretchr/testify/require"
+)
+
+func TestUpstreamDoH(t *testing.T) {
+	testCases := []struct {
+		name             string
+		http3Enabled     bool
+		httpVersions     []HTTPVersion
+		delayHandshakeH3 time.Duration
+		delayHandshakeH2 time.Duration
+		expectedProtocol HTTPVersion
+	}{{
+		name:             "http1.1_h2",
+		http3Enabled:     false,
+		httpVersions:     []HTTPVersion{HTTPVersion11, HTTPVersion2},
+		expectedProtocol: HTTPVersion2,
+	}, {
+		name:             "fallback_to_http2",
+		http3Enabled:     false,
+		httpVersions:     []HTTPVersion{HTTPVersion3, HTTPVersion2},
+		expectedProtocol: HTTPVersion2,
+	}, {
+		name:             "http3",
+		http3Enabled:     true,
+		httpVersions:     []HTTPVersion{HTTPVersion3},
+		expectedProtocol: HTTPVersion3,
+	}, {
+		name:             "race_http3_faster",
+		http3Enabled:     true,
+		httpVersions:     []HTTPVersion{HTTPVersion3, HTTPVersion2},
+		delayHandshakeH2: time.Second,
+		expectedProtocol: HTTPVersion3,
+	}, {
+		name:             "race_http2_faster",
+		http3Enabled:     true,
+		httpVersions:     []HTTPVersion{HTTPVersion3, HTTPVersion2},
+		delayHandshakeH3: time.Second,
+		expectedProtocol: HTTPVersion2,
+	}}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			srv := startDoHServer(t, testDoHServerOptions{
+				http3Enabled:     tc.http3Enabled,
+				delayHandshakeH2: tc.delayHandshakeH2,
+				delayHandshakeH3: tc.delayHandshakeH3,
+			})
+			t.Cleanup(srv.Shutdown)
+
+			// Create a DNS-over-HTTPS upstream.
+			address := fmt.Sprintf("https://%s/dns-query", srv.addr)
+
+			var lastState tls.ConnectionState
+			u, err := AddressToUpstream(
+				address,
+				&Options{
+					InsecureSkipVerify: true,
+					HTTPVersions:       tc.httpVersions,
+					VerifyConnection: func(state tls.ConnectionState) (err error) {
+						if state.NegotiatedProtocol != string(tc.expectedProtocol) {
+							return fmt.Errorf(
+								"expected %s, got %s",
+								tc.expectedProtocol,
+								state.NegotiatedProtocol,
+							)
+						}
+						lastState = state
+						return nil
+					},
+				},
+			)
+			require.NoError(t, err)
+
+			// Test that it responds properly.
+			for i := 0; i < 10; i++ {
+				checkUpstream(t, u, address)
+			}
+
+			doh := u.(*dnsOverHTTPS)
+
+			// Trigger re-connection.
+			doh.client = nil
+
+			// Force it to establish the connection again.
+			checkUpstream(t, u, address)
+
+			// Check that TLS session was resumed properly.
+			require.True(t, lastState.DidResume)
+		})
+	}
+}
+
+// testDoHServerOptions allows customizing testDoHServer behavior.
+type testDoHServerOptions struct {
+	http3Enabled     bool
+	delayHandshakeH2 time.Duration
+	delayHandshakeH3 time.Duration
+}
+
+// testDoHServer is an instance of a test DNS-over-HTTPS server that we use
+// for tests.
+type testDoHServer struct {
+	// addr is the address that this server listens to.
+	addr string
+
+	// tlsConfig is the TLS configuration that is used for this server.
+	tlsConfig *tls.Config
+
+	// server is an HTTP/1.1 and HTTP/2 server.
+	server *http.Server
+
+	// serverH3 is an HTTP/3 server.
+	serverH3 *http3.Server
+}
+
+// Shutdown stops the DOH server.
+func (s *testDoHServer) Shutdown() {
+	if s.server != nil {
+		_ = s.server.Shutdown(context.Background())
+	}
+
+	if s.serverH3 != nil {
+		_ = s.serverH3.Close()
+	}
+}
+
+// startDoHServer starts a new DNS-over-HTTPS server on a random port and
+// returns the instance of this server.  Depending on whether http3Enabled is
+// set to true or false it will or will not initialize a HTTP/3 server.
+func startDoHServer(
+	t *testing.T,
+	opts testDoHServerOptions,
+) (s *testDoHServer) {
+	tlsConfig := createServerTLSConfig(t, "127.0.0.1")
+	handler := createDoHHandler()
+
+	// Step one is to create a regular HTTP server, we'll always have it
+	// running.
+	server := &http.Server{
+		Handler: handler,
+	}
+
+	// Listen TCP first.
+	tcpAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:0")
+	require.NoError(t, err)
+
+	tcpListen, err := net.ListenTCP("tcp", tcpAddr)
+	require.NoError(t, err)
+
+	tlsConfigH2 := tlsConfig.Clone()
+	tlsConfigH2.NextProtos = []string{string(HTTPVersion2), string(HTTPVersion11)}
+	tlsConfigH2.GetConfigForClient = func(_ *tls.ClientHelloInfo) (*tls.Config, error) {
+		if opts.delayHandshakeH2 > 0 {
+			time.Sleep(opts.delayHandshakeH2)
+		}
+		return nil, nil
+	}
+	tlsListen := tls.NewListener(tcpListen, tlsConfigH2)
+
+	// Run the H1/H2 server.
+	go server.Serve(tlsListen)
+
+	// Get the real address that the listener now listens to.
+	tcpAddr = tcpListen.Addr().(*net.TCPAddr)
+
+	var serverH3 *http3.Server
+
+	if opts.http3Enabled {
+		tlsConfigH3 := tlsConfig.Clone()
+		tlsConfigH3.NextProtos = []string{string(HTTPVersion3)}
+		tlsConfigH3.GetConfigForClient = func(_ *tls.ClientHelloInfo) (*tls.Config, error) {
+			if opts.delayHandshakeH3 > 0 {
+				time.Sleep(opts.delayHandshakeH3)
+			}
+			return nil, nil
+		}
+
+		serverH3 = &http3.Server{
+			TLSConfig:  tlsConfig.Clone(),
+			QuicConfig: &quic.Config{},
+			Handler:    handler,
+		}
+
+		// Listen UDP for the H3 server. Reuse the same port as was used for the
+		// TCP listener.
+		udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", tcpAddr.Port))
+		require.NoError(t, err)
+
+		udpListen, err := net.ListenUDP("udp", udpAddr)
+		require.NoError(t, err)
+
+		// Run the H3 server.
+		go serverH3.Serve(udpListen)
+	}
+
+	return &testDoHServer{
+		tlsConfig: tlsConfig,
+		server:    server,
+		serverH3:  serverH3,
+		// Save the address that the server listens to.
+		addr: tcpAddr.String(),
+	}
+}
+
+// createDoHHandler returns a very simple http.Handler that reads the incoming
+// request and returns with a test message.
+func createDoHHandler() (h http.Handler) {
+	mux := http.NewServeMux()
+	mux.HandleFunc("/dns-query", func(w http.ResponseWriter, r *http.Request) {
+		dnsParam := r.URL.Query().Get("dns")
+		buf, err := base64.RawURLEncoding.DecodeString(dnsParam)
+		if err != nil {
+			http.Error(
+				w,
+				fmt.Sprintf("internal error: %s", err),
+				http.StatusInternalServerError,
+			)
+			return
+		}
+
+		m := &dns.Msg{}
+		err = m.Unpack(buf)
+		if err != nil {
+			http.Error(
+				w,
+				fmt.Sprintf("internal error: %s", err),
+				http.StatusInternalServerError,
+			)
+			return
+		}
+
+		resp := respondToTestMessage(m)
+
+		buf, err = resp.Pack()
+		if err != nil {
+			http.Error(
+				w,
+				fmt.Sprintf("internal error: %s", err),
+				http.StatusInternalServerError,
+			)
+			return
+		}
+
+		w.Header().Set("Content-Type", "application/dns-message")
+		_, err = w.Write(buf)
+	})
+
+	return mux
+}
diff --git a/upstream/upstream_dot.go b/upstream/upstream_dot.go
index 4b31d330..380478d3 100644
--- a/upstream/upstream_dot.go
+++ b/upstream/upstream_dot.go
@@ -11,9 +11,8 @@ import (
 	"github.com/miekg/dns"
 )
 
-//
-// DNS-over-TLS
-//
+// dnsOverTLS is a struct that implements the Upstream interface for the
+// DNS-over-TLS protocol.
 type dnsOverTLS struct {
 	boot *bootstrapper
 	pool *TLSPool
@@ -37,8 +36,10 @@ func newDoT(uu *url.URL, opts *Options) (u Upstream, err error) {
 	return &dnsOverTLS{boot: b}, nil
 }
 
+// Address implements the Upstream interface for *dnsOverTLS.
 func (p *dnsOverTLS) Address() string { return p.boot.URL.String() }
 
+// Exchange implements the Upstream interface for *dnsOverTLS.
 func (p *dnsOverTLS) Exchange(m *dns.Msg) (reply *dns.Msg, err error) {
 	var pool *TLSPool
 	p.RLock()
diff --git a/upstream/upstream_plain.go b/upstream/upstream_plain.go
index 1b6d1781..f5f4efd6 100644
--- a/upstream/upstream_plain.go
+++ b/upstream/upstream_plain.go
@@ -8,9 +8,8 @@ import (
 	"github.com/miekg/dns"
 )
 
-//
-// plain DNS
-//
+// plainDNS is a struct that implements the Upstream interface for the regular
+// DNS protocol.
 type plainDNS struct {
 	address   string
 	timeout   time.Duration
@@ -31,7 +30,7 @@ func newPlain(uu *url.URL, timeout time.Duration, preferTCP bool) (u *plainDNS)
 	}
 }
 
-// Address returns the original address that we've put in initially, not resolved one
+// Address implements the Upstream interface for *plainDNS.
 func (p *plainDNS) Address() string {
 	if p.preferTCP {
 		return "tcp://" + p.address
@@ -40,6 +39,7 @@ func (p *plainDNS) Address() string {
 	return p.address
 }
 
+// Exchange implements the Upstream interface for *plainDNS.
 func (p *plainDNS) Exchange(m *dns.Msg) (*dns.Msg, error) {
 	if p.preferTCP {
 		tcpClient := dns.Client{Net: "tcp", Timeout: p.timeout}
diff --git a/upstream/upstream_pool.go b/upstream/upstream_pool.go
index 276fa04f..9c3ddd3b 100644
--- a/upstream/upstream_pool.go
+++ b/upstream/upstream_pool.go
@@ -11,35 +11,40 @@ import (
 	"github.com/AdguardTeam/golibs/log"
 )
 
+// dialTimeout is the global timeout for establishing a TLS connection.
+// TODO(ameshkov): use bootstrap timeout instead.
 const dialTimeout = 10 * time.Second
 
 // TLSPool is a connections pool for the DNS-over-TLS Upstream.
 //
 // Example:
-//  pool := TLSPool{Address: "tls://1.1.1.1:853"}
-//  netConn, err := pool.Get()
-//  if err != nil {panic(err)}
-//  c := dns.Conn{Conn: netConn}
-//  q := dns.Msg{}
-//  q.SetQuestion("google.com.", dns.TypeA)
-//  log.Println(q)
-//  err = c.WriteMsg(&q)
-//  if err != nil {panic(err)}
-//  r, err := c.ReadMsg()
-//  if err != nil {panic(err)}
-//  log.Println(r)
-//  pool.Put(c.Conn)
+//
+//	pool := TLSPool{Address: "tls://1.1.1.1:853"}
+//	netConn, err := pool.Get()
+//	if err != nil {panic(err)}
+//	c := dns.Conn{Conn: netConn}
+//	q := dns.Msg{}
+//	q.SetQuestion("google.com.", dns.TypeA)
+//	log.Println(q)
+//	err = c.WriteMsg(&q)
+//	if err != nil {panic(err)}
+//	r, err := c.ReadMsg()
+//	if err != nil {panic(err)}
+//	log.Println(r)
+//	pool.Put(c.Conn)
 type TLSPool struct {
 	boot *bootstrapper
 
-	// connections
-	conns      []net.Conn
-	connsMutex sync.Mutex // protects conns
+	// conns is the list of connections available in the pool.
+	conns []net.Conn
+	// connsMutex protects conns.
+	connsMutex sync.Mutex
 }
 
-// Get gets or creates a new TLS connection
+// Get gets a connection from the pool (if there's one available) or creates
+// a new TLS connection.
 func (n *TLSPool) Get() (net.Conn, error) {
-	// get the connection from the slice inside the lock
+	// Get the connection from the slice inside the lock.
 	var c net.Conn
 	n.connsMutex.Lock()
 	num := len(n.conns)
@@ -50,7 +55,7 @@ func (n *TLSPool) Get() (net.Conn, error) {
 	}
 	n.connsMutex.Unlock()
 
-	// if we got connection from the slice, update deadline and return it.
+	// If we got connection from the slice, update deadline and return it.
 	if c != nil {
 		err := c.SetDeadline(time.Now().Add(dialTimeout))
 
@@ -64,7 +69,7 @@ func (n *TLSPool) Get() (net.Conn, error) {
 	return n.Create()
 }
 
-// Create creates a new connection for the pool (but not puts it there)
+// Create creates a new connection for the pool (but not puts it there).
 func (n *TLSPool) Create() (net.Conn, error) {
 	tlsConfig, dialContext, err := n.boot.get()
 	if err != nil {
@@ -80,7 +85,7 @@ func (n *TLSPool) Create() (net.Conn, error) {
 	return conn, nil
 }
 
-// Put returns connection to the pool
+// Put returns the connection to the pool.
 func (n *TLSPool) Put(c net.Conn) {
 	if c == nil {
 		return
@@ -90,16 +95,18 @@ func (n *TLSPool) Put(c net.Conn) {
 	n.connsMutex.Unlock()
 }
 
-// tlsDial is basically the same as tls.DialWithDialer, but we will call our own dialContext function to get connection
+// tlsDial is basically the same as tls.DialWithDialer, but we will call our own
+// dialContext function to get connection.
 func tlsDial(dialContext dialHandler, network string, config *tls.Config) (*tls.Conn, error) {
-	// we're using bootstrapped address instead of what's passed to the function
+	// We're using bootstrapped address instead of what's passed
+	// to the function.
 	rawConn, err := dialContext(context.Background(), network, "")
 	if err != nil {
 		return nil, err
 	}
 
-	// we want the timeout to cover the whole process: TCP connection and TLS handshake
-	// dialTimeout will be used as connection deadLine
+	// We want the timeout to cover the whole process: TCP connection and
+	// TLS handshake dialTimeout will be used as connection deadLine.
 	conn := tls.Client(rawConn, config)
 	err = conn.SetDeadline(time.Now().Add(dialTimeout))
 	if err != nil {
diff --git a/upstream/upstream_pool_test.go b/upstream/upstream_pool_test.go
index fe59edfa..85b73ad3 100644
--- a/upstream/upstream_pool_test.go
+++ b/upstream/upstream_pool_test.go
@@ -1,48 +1,51 @@
 package upstream
 
 import (
+	"crypto/tls"
 	"testing"
 	"time"
+
+	"github.com/stretchr/testify/require"
 )
 
 func TestTLSPoolReconnect(t *testing.T) {
+	var lastState tls.ConnectionState
 	u, err := AddressToUpstream(
 		"tls://one.one.one.one",
 		&Options{
 			Bootstrap: []string{"8.8.8.8:53"},
 			Timeout:   timeout,
+			VerifyConnection: func(state tls.ConnectionState) error {
+				lastState = state
+				return nil
+			},
 		},
 	)
-	if err != nil {
-		t.Fatalf("cannot create upstream: %s", err)
-	}
+	require.NoError(t, err)
 
-	// Send the first test message
+	// Send the first test message.
 	req := createTestMessage()
 	reply, err := u.Exchange(req)
-	if err != nil {
-		t.Fatalf("first DNS message failed: %s", err)
-	}
+	require.NoError(t, err)
 	requireResponse(t, req, reply)
 
-	// Now let's close the pooled connection and return it back to the pool
+	// Now let's close the pooled connection and return it back to the pool.
 	p := u.(*dnsOverTLS)
 	conn, _ := p.pool.Get()
 	conn.Close()
 	p.pool.Put(conn)
 
-	// Send the second test message
+	// Send the second test message.
 	req = createTestMessage()
 	reply, err = u.Exchange(req)
-	if err != nil {
-		t.Fatalf("second DNS message failed: %s", err)
-	}
+	require.NoError(t, err)
 	requireResponse(t, req, reply)
 
 	// Now assert that the number of connections in the pool is not changed
-	if len(p.pool.conns) != 1 {
-		t.Fatal("wrong number of pooled connections")
-	}
+	require.Len(t, p.pool.conns, 1)
+
+	// Check that the session was resumed on the last attempt.
+	require.True(t, lastState.DidResume)
 }
 
 func TestTLSPoolDeadLine(t *testing.T) {
diff --git a/upstream/upstream_quic.go b/upstream/upstream_quic.go
index e4ca6df3..eb98fd17 100644
--- a/upstream/upstream_quic.go
+++ b/upstream/upstream_quic.go
@@ -15,30 +15,37 @@ import (
 )
 
 const (
-	// DoQCodeNoError is used when the connection or stream needs to be closed,
+	// QUICCodeNoError is used when the connection or stream needs to be closed,
 	// but there is no error to signal.
-	DoQCodeNoError = quic.ApplicationErrorCode(0)
-	// DoQCodeInternalError signals that the DoQ implementation encountered
+	QUICCodeNoError = quic.ApplicationErrorCode(0)
+	// QUICCodeInternalError signals that the DoQ implementation encountered
 	// an internal error and is incapable of pursuing the transaction or the
 	// connection.
-	DoQCodeInternalError = quic.ApplicationErrorCode(1)
-	// DoQCodeProtocolError signals that the DoQ implementation encountered
+	QUICCodeInternalError = quic.ApplicationErrorCode(1)
+	// QUICCodeProtocolError signals that the DoQ implementation encountered
 	// a protocol error and is forcibly aborting the connection.
-	DoQCodeProtocolError = quic.ApplicationErrorCode(2)
+	QUICCodeProtocolError = quic.ApplicationErrorCode(2)
+	// QUICKeepAlivePeriod is the value that we pass to *quic.Config and that
+	// controls the period with with keep-alive frames are being sent to the
+	// connection. We set it to 20s as it would be in the quic-go@v0.27.1 with
+	// KeepAlive field set to true This value is specified in
+	// https://pkg.go.dev/github.com/lucas-clemente/quic-go/internal/protocol#MaxKeepAliveInterval.
+	//
+	// TODO(ameshkov):  Consider making it configurable.
+	QUICKeepAlivePeriod = time.Second * 20
 )
 
-//
-// dnsOverQUIC is a DNS-over-QUIC implementation according to the spec:
-// https://www.rfc-editor.org/rfc/rfc9250.html
-//
+// dnsOverQUIC is a struct that implements the Upstream interface for the
+// DNS-over-QUIC protocol (spec: https://www.rfc-editor.org/rfc/rfc9250.html).
 type dnsOverQUIC struct {
 	// boot is a bootstrap DNS abstraction that is used to resolve the upstream
 	// server's address and open a network connection to it.
 	boot *bootstrapper
-	// tokenStore is a QUIC token store that is used across QUIC connections.
-	// Since the QUIC config is re-created when a connection is (re-)opened
-	// the tokenStore is instead saved as part of the dnsOverQUIC struct.
-	tokenStore quic.TokenStore
+	// quicConfig is the QUIC configuration that is used for establishing
+	// connections to the upstream.  This configuration includes the TokenStore
+	// that needs to be stored for the lifetime of dnsOverQUIC since we can
+	// re-create the connection.
+	quicConfig *quic.Config
 	// conn is the current active QUIC connection.  It can be closed and
 	// re-opened when needed.
 	conn quic.Connection
@@ -62,11 +69,24 @@ func newDoQ(uu *url.URL, opts *Options) (u Upstream, err error) {
 		return nil, fmt.Errorf("creating quic bootstrapper: %w", err)
 	}
 
-	return &dnsOverQUIC{boot: b, tokenStore: quic.NewLRUTokenStore(1, 10)}, nil
+	return &dnsOverQUIC{
+		boot: b,
+		quicConfig: &quic.Config{
+			KeepAlivePeriod: QUICKeepAlivePeriod,
+			// You can read more on address validation here:
+			// https://datatracker.ietf.org/doc/html/rfc9000#section-8.1
+			// Setting maxOrigins to 1 and tokensPerOrigin to 10 assuming that
+			// this is more than enough for the way we use it (one connection
+			// per upstream).
+			TokenStore: quic.NewLRUTokenStore(1, 10),
+		},
+	}, nil
 }
 
+// Address implements the Upstream interface for *dnsOverQUIC.
 func (p *dnsOverQUIC) Address() string { return p.boot.URL.String() }
 
+// Exchange implements the Upstream interface for *dnsOverQUIC.
 func (p *dnsOverQUIC) Exchange(m *dns.Msg) (res *dns.Msg, err error) {
 	var conn quic.Connection
 	conn, err = p.getConnection(true)
@@ -95,13 +115,13 @@ func (p *dnsOverQUIC) Exchange(m *dns.Msg) (res *dns.Msg, err error) {
 	var stream quic.Stream
 	stream, err = p.openStream(conn)
 	if err != nil {
-		p.closeConnWithError(DoQCodeInternalError)
+		p.closeConnWithError(QUICCodeInternalError)
 		return nil, fmt.Errorf("open new stream to %s: %w", p.Address(), err)
 	}
 
 	_, err = stream.Write(proxyutil.AddPrefix(buf))
 	if err != nil {
-		p.closeConnWithError(DoQCodeInternalError)
+		p.closeConnWithError(QUICCodeInternalError)
 		return nil, fmt.Errorf("failed to write to a QUIC stream: %w", err)
 	}
 
@@ -117,7 +137,7 @@ func (p *dnsOverQUIC) Exchange(m *dns.Msg) (res *dns.Msg, err error) {
 		// fatal error.  It SHOULD forcibly abort the connection using QUIC's
 		// CONNECTION_CLOSE mechanism and SHOULD use the DoQ error code
 		// DOQ_PROTOCOL_ERROR.
-		p.closeConnWithError(DoQCodeProtocolError)
+		p.closeConnWithError(QUICCodeProtocolError)
 	}
 	return res, err
 }
@@ -152,7 +172,7 @@ func (p *dnsOverQUIC) getConnection(useCached bool) (quic.Connection, error) {
 	}
 	if conn != nil {
 		// we're recreating the connection, let's create a new one.
-		_ = conn.CloseWithError(DoQCodeNoError, "")
+		_ = conn.CloseWithError(QUICCodeNoError, "")
 	}
 	p.RUnlock()
 
@@ -162,11 +182,10 @@ func (p *dnsOverQUIC) getConnection(useCached bool) (quic.Connection, error) {
 	var err error
 	conn, err = p.openConnection()
 	if err != nil {
-		// This does not look too nice, but QUIC (or maybe quic-go)
-		// doesn't seem stable enough.
-		// Maybe retransmissions aren't fully implemented in quic-go?
-		// Anyways, the simple solution is to make a second try when
-		// it fails to open the QUIC conn.
+		// This does not look too nice, but QUIC (or maybe quic-go) doesn't
+		// seem stable enough. Maybe retransmissions aren't fully implemented
+		// in quic-go? Anyways, the simple solution is to make a second try when
+		// it fails to open the QUIC connection.
 		conn, err = p.openConnection()
 		if err != nil {
 			return nil, err
@@ -224,17 +243,8 @@ func (p *dnsOverQUIC) openConnection() (conn quic.Connection, err error) {
 	}
 
 	addr := udpConn.RemoteAddr().String()
-	quicConfig := &quic.Config{
-		// Set the keep alive interval to 20s as it would be in the
-		// quic-go@v0.27.1 with KeepAlive field set to true.  This value is
-		// specified in
-		// https://pkg.go.dev/github.com/lucas-clemente/quic-go/internal/protocol#MaxKeepAliveInterval.
-		//
-		// TODO(ameshkov):  Consider making it configurable.
-		KeepAlivePeriod: 20 * time.Second,
-		TokenStore:	p.tokenStore,
-	}
-	conn, err = quic.DialAddrEarlyContext(context.Background(), addr, tlsConfig, quicConfig)
+
+	conn, err = quic.DialAddrEarlyContext(context.Background(), addr, tlsConfig, p.quicConfig)
 	if err != nil {
 		return nil, fmt.Errorf("opening quic connection to %s: %w", p.Address(), err)
 	}
diff --git a/upstream/upstream_quic_test.go b/upstream/upstream_quic_test.go
index f5d2ac5b..0a2fe21f 100644
--- a/upstream/upstream_quic_test.go
+++ b/upstream/upstream_quic_test.go
@@ -1,6 +1,7 @@
 package upstream
 
 import (
+	"crypto/tls"
 	"testing"
 
 	"github.com/lucas-clemente/quic-go"
@@ -10,7 +11,16 @@ import (
 func TestUpstreamDoQ(t *testing.T) {
 	// Create a DNS-over-QUIC upstream
 	address := "quic://dns.adguard.com"
-	u, err := AddressToUpstream(address, &Options{InsecureSkipVerify: true})
+	var lastState tls.ConnectionState
+	u, err := AddressToUpstream(
+		address,
+		&Options{
+			VerifyConnection: func(state tls.ConnectionState) error {
+				lastState = state
+				return nil
+			},
+		},
+	)
 	require.NoError(t, err)
 
 	uq := u.(*dnsOverQUIC)
@@ -23,8 +33,17 @@ func TestUpstreamDoQ(t *testing.T) {
 		if conn == nil {
 			conn = uq.conn
 		} else {
-			// This way we test that the conn is properly reused
+			// This way we test that the conn is properly reused.
 			require.Equal(t, conn, uq.conn)
 		}
 	}
+
+	// Close the connection (make sure that we re-establish the connection).
+	_ = conn.CloseWithError(quic.ApplicationErrorCode(0), "")
+
+	// Try to establish it again.
+	checkUpstream(t, u, address)
+
+	// Make sure that the session has been resumed.
+	require.True(t, lastState.DidResume)
 }
diff --git a/upstream/upstream_test.go b/upstream/upstream_test.go
index 4a0901c4..dcf9e19a 100644
--- a/upstream/upstream_test.go
+++ b/upstream/upstream_test.go
@@ -1,8 +1,16 @@
 package upstream
 
 import (
+	"crypto/ecdsa"
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/tls"
+	"crypto/x509"
+	"crypto/x509/pkix"
+	"encoding/pem"
 	"fmt"
 	"io"
+	"math/big"
 	"net"
 	"net/url"
 	"os"
@@ -192,6 +200,10 @@ func TestUpstreams(t *testing.T) {
 		// Cloudflare DNS
 		address:   "quic://dns-unfiltered.adguard.com:784",
 		bootstrap: []string{},
+	}, {
+		// Google DNS (HTTP3)
+		address:   "h3://dns.google/dns-query",
+		bootstrap: []string{},
 	}}
 	for _, test := range upstreams {
 		t.Run(test.address, func(t *testing.T) {
@@ -237,6 +249,10 @@ func TestAddressToUpstream(t *testing.T) {
 		addr: "https://one.one.one.one",
 		opt:  opt,
 		want: "https://one.one.one.one:443",
+	}, {
+		addr: "h3://one.one.one.one",
+		opt:  opt,
+		want: "https://one.one.one.one:443",
 	}}
 
 	for _, tc := range testCases {
@@ -405,45 +421,6 @@ func TestUpstreamsWithServerIP(t *testing.T) {
 	}
 }
 
-func checkUpstream(t *testing.T, u Upstream, addr string) {
-	t.Helper()
-
-	req := createTestMessage()
-	reply, err := u.Exchange(req)
-	require.NoErrorf(t, err, "couldn't talk to upstream %s", addr)
-
-	requireResponse(t, req, reply)
-}
-
-func createTestMessage() *dns.Msg {
-	return createHostTestMessage("google-public-dns-a.google.com")
-}
-
-func createHostTestMessage(host string) (req *dns.Msg) {
-	return &dns.Msg{
-		MsgHdr: dns.MsgHdr{
-			Id:               dns.Id(),
-			RecursionDesired: true,
-		},
-		Question: []dns.Question{{
-			Name:   dns.Fqdn(host),
-			Qtype:  dns.TypeA,
-			Qclass: dns.ClassINET,
-		}},
-	}
-}
-
-func requireResponse(t *testing.T, req, reply *dns.Msg) {
-	require.NotNil(t, reply)
-	require.Lenf(t, reply.Answer, 1, "wrong number of answers: %d", len(reply.Answer))
-	require.Equal(t, req.Id, reply.Id)
-
-	a, ok := reply.Answer[0].(*dns.A)
-	require.Truef(t, ok, "wrong answer type: %v", reply.Answer[0])
-
-	require.Equalf(t, net.IPv4(8, 8, 8, 8), a.A.To16(), "wrong answer: %v", a.A)
-}
-
 func TestAddPort(t *testing.T) {
 	testCases := []struct {
 		name string
@@ -498,3 +475,131 @@ func TestAddPort(t *testing.T) {
 		})
 	}
 }
+
+func checkUpstream(t *testing.T, u Upstream, addr string) {
+	t.Helper()
+
+	req := createTestMessage()
+	reply, err := u.Exchange(req)
+	require.NoErrorf(t, err, "couldn't talk to upstream %s", addr)
+
+	requireResponse(t, req, reply)
+}
+
+func createTestMessage() (m *dns.Msg) {
+	return createHostTestMessage("google-public-dns-a.google.com")
+}
+
+func respondToTestMessage(m *dns.Msg) (resp *dns.Msg) {
+	resp = &dns.Msg{}
+	resp.SetReply(m)
+	resp.Answer = append(resp.Answer, &dns.A{
+		A: net.IPv4(8, 8, 8, 8),
+		Hdr: dns.RR_Header{
+			Name:   "google-public-dns-a.google.com.",
+			Rrtype: dns.TypeA,
+			Class:  dns.ClassINET,
+			Ttl:    100,
+		},
+	})
+
+	return resp
+}
+
+func createHostTestMessage(host string) (req *dns.Msg) {
+	return &dns.Msg{
+		MsgHdr: dns.MsgHdr{
+			Id:               dns.Id(),
+			RecursionDesired: true,
+		},
+		Question: []dns.Question{{
+			Name:   dns.Fqdn(host),
+			Qtype:  dns.TypeA,
+			Qclass: dns.ClassINET,
+		}},
+	}
+}
+
+func requireResponse(t *testing.T, req, reply *dns.Msg) {
+	require.NotNil(t, reply)
+	require.Lenf(t, reply.Answer, 1, "wrong number of answers: %d", len(reply.Answer))
+	require.Equal(t, req.Id, reply.Id)
+
+	a, ok := reply.Answer[0].(*dns.A)
+	require.Truef(t, ok, "wrong answer type: %v", reply.Answer[0])
+
+	require.Equalf(t, net.IPv4(8, 8, 8, 8), a.A.To16(), "wrong answer: %v", a.A)
+}
+
+// createServerTLSConfig creates a test server TLS configuration. It returns
+// a *tls.Config that can be used for both the server and the client.
+func createServerTLSConfig(t *testing.T, tlsServerName string) (tlsConfig *tls.Config) {
+	privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
+	require.NoError(t, err)
+
+	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
+	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
+	require.NoError(t, err)
+
+	notBefore := time.Now()
+	notAfter := notBefore.Add(5 * 365 * time.Hour * 24)
+
+	template := x509.Certificate{
+		SerialNumber: serialNumber,
+		Subject: pkix.Name{
+			Organization: []string{"AdGuard Tests"},
+		},
+		NotBefore: notBefore,
+		NotAfter:  notAfter,
+
+		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
+		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
+		BasicConstraintsValid: true,
+		IsCA:                  true,
+	}
+	template.DNSNames = append(template.DNSNames, tlsServerName)
+
+	derBytes, err := x509.CreateCertificate(
+		rand.Reader,
+		&template,
+		&template,
+		publicKey(privateKey),
+		privateKey,
+	)
+	require.NoError(t, err)
+
+	certPem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
+	keyPem := pem.EncodeToMemory(
+		&pem.Block{
+			Type:  "RSA PRIVATE KEY",
+			Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
+		},
+	)
+
+	cert, err := tls.X509KeyPair(certPem, keyPem)
+	require.NoError(t, err)
+
+	roots := x509.NewCertPool()
+	roots.AppendCertsFromPEM(certPem)
+
+	tlsConfig = &tls.Config{
+		Certificates: []tls.Certificate{cert},
+		ServerName:   tlsServerName,
+		RootCAs:      roots,
+		MinVersion:   tls.VersionTLS12,
+	}
+
+	return tlsConfig
+}
+
+// publicKey extracts the public key from the specified private key.
+func publicKey(priv any) (pub any) {
+	switch k := priv.(type) {
+	case *rsa.PrivateKey:
+		return &k.PublicKey
+	case *ecdsa.PrivateKey:
+		return &k.PublicKey
+	default:
+		return nil
+	}
+}
diff --git a/vendor/github.com/lucas-clemente/quic-go/http3/body.go b/vendor/github.com/lucas-clemente/quic-go/http3/body.go
new file mode 100644
index 0000000000000000000000000000000000000000..d6e704ebcb169646fb06c040c6e7b4c1a01ce3dd
GIT binary patch
literal 2970
zcmXR&OwLYBPgTe$DJd}K;>ye|$S*2U(BR@!O3u$KNv$YR;^I`w%m<NqsU=EWT%1bj
znI##eNqWiox%xS!$%)0f$vLUHsd*)-`h}&L$-3$JN?e*;T>AP7AsML(9w8wC!6ikh
ziMgpo3W+&6`Q^n5C5hRYdFcxIWgsE23VmaRVz44Tu%^tslGLKK#N<?k%wmPi+ybci
zDGEuIRv?456w>mG6iPBu6^c`f%2JE0z-*8g^ny|gOH+$W^qlfjDp8dt=VYelm7pjs
zF38U-PKB%W&jXtSQK(RynUbml7JxWSp*%AuM<FRyAvq_%I5h?Agz}=ylFYnx9fi`o
zoYdlCuxgk5yb@=S5~x!kzE=p!FU?B{DatJHFDOwcPb^j_PA$;`xiCB<HBTWC>VJ@j
zOA@nF^T45_qmWsm4ly-3F()UrNWEC0C>8AL%%sem%#uoll6;7Jz*a*Xs>fAQS%4Pu
zh!Cyj;zY!WhNc3<d0e$z5M2<ib5UwyNq&(6iZ=g()I5l^hNglBD6aJ&TpfkfqN4mF
zO)gG^lHkg`WDRH%(sPC+C49O<^D-fZA<RzAOMwWXnh#fr*9t6}ee#nNa~xAriZnD8
z@={CmKuj*qpw!&_l2i~IS-6&qt1PidAs!k$u<)=|0DIdxKQAvexg;|`Pa`ig2a?zw
z6+AMt5|gt*X&REuGr)XsN>4+I9))C3;DUq;5{pU{^3xO&A*KeW78hsc=P9HW<>x9S
zf^(!^5G1xCX%w7XJWD`nq!g3_lPVR$Q<Fl967!0|xfYZbKxU_+3PVywu|i^63CM8@
z#ZY@P5{ng*Qd9F3Qj1FxlX5bPGg4DvX$S1|RI~&Di4AC2z>^M?4@yDMJfD=GQVH=F
za+XxcFGBMZB*lSM7nc;3CYLBwb8!}z6hVUnTp)09ah4|*LxVO&At^sUM?qg7lz9}&
zK^X^;mO;S+&Lt@zr+{KlgNrj>Av0euC^az!oFj`AY!%c%_Epz{q!4;jixiNgG`YCa
zO7oHx@>0v8c^FihXn<S>2?9+8Es!Z7=N6@wlosVdO)V}dvI1q~T96CCMrag4lxl*D
zIf!m}$bt3iA$!kOp`@rZm5UQ<YLQ-XNf8#a$`gwrM!~{GLlY7*)ks>A%*A2^C`>ex
z6ry93Doau|6*MyQN?_Rx>@0}4zzzV1pGJ}<7E{2Xs-X$V|6qL}7wb7E<|U`*fUN-M
z7l>nBL4|~KeoCqaxVV4@Ektc<il!#q?Rl9wAiKb&7$g%VCgr4p;s6x5&~yTd04~nt
z{JcC!azW%tQ2NNv%c)ezOHEBlO#u~=@HzyZNx8T<_4O6N%8FA<z=b)eK7l4!a1omd
zRsyQTk~0$X@=|laDGXGyr$EvkNJD8cD3=r~q~zzNDwJoIWGIwmWWr17R4cF<shK4i
zsYMV?;81~vSg{T$Q-ISkEF?3F6(EUN4-_1QE+AtSzyRb38(nA!fD&*~Dp(q<6OuDP
zp$1R*(6mtm)@-Ywh8mI}1LBbqWhyvtV5mXK3Xo6%#RF1a&{4n^2Re`dL36XFf)=U`
z;3NY{YUu9c;^YKHv6TXdtmfk61eFZn+@-_C$yo~`paEb7whJTxa)}klzaWMV7bj{y
zM70`L^FfO*c&3E%kz7`!2Qm&6w(u;CZWK6Dap$HyaMn_=vQ<FMP@I`*psb^yXseKy
znFF$tvq-N9(t-iSyoM&o53tezVhUb&py&memzkyjHW6eRG@;bgAcPcbk!%LX1K2`j
zUC53Dw|q3<20~p2axs$m&@!wRT8JV_Ku9Uz>7t<t$+0j#$eqXq9<)^fQi#{HVE>>N
zJ}CYNXFX3y)$8i-25xi}r6#86C_u^_aHMI1GIgRtUcPRA0VvB+vHZfBS`hUPJk_Cx
I0aq;-0H1~8LI3~&

literal 0
HcmV?d00001

diff --git a/vendor/github.com/lucas-clemente/quic-go/http3/client.go b/vendor/github.com/lucas-clemente/quic-go/http3/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..90115e4b1b82926b8ebfc9edcc466c733c3aae33
GIT binary patch
literal 12686
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
X3Pk5LwHTazK}~0H6;fTxRm%kcR&g2e

literal 0
HcmV?d00001

diff --git a/vendor/github.com/lucas-clemente/quic-go/http3/error_codes.go b/vendor/github.com/lucas-clemente/quic-go/http3/error_codes.go
new file mode 100644
index 0000000000000000000000000000000000000000..d87eef4ae076a40de382cee10cb5833b0e2a9d8f
GIT binary patch
literal 2089
zcmXR&OwLYBPgTe$DJd}K;>ye|$S*2U(BR@!O3N)#;^N{|O3y6GC{5B!&d=4)DNRl+
z)=kby%}vcKN!2eb%}mx!&sXBo<l-u+EJ#&IEh@?{a?VdlRRBrpITjS;WF{w;Waj6&
z!eqF(lJoP5!8U+Z`{jeB6i@(I25gS4f<c9$fdLmMSjas!FSRH!C!i?5BtJPn2ci=!
zgQVCHs@OBHB(*3n5vmGmKDuHfsN&#~qSVA(=b}`EYZZ{Z1965iRIzhTesOAwb5UkV
zW^!T<L@S136R2XhqQu<P(7e=&g4E=a)D(z2(4Aq5tQg`gh(ZvFuGkE!*tH@#wYWI5
zEY&AJ5o#``o6Vt$Jzda^Rlrni0aYBFT2hjkmtKsb1>G5zP{qEP#l@L<=`cMY7h{^7
z2vr=ET3DJ|ToRO;g(ozUP!&5T<|U`*;0n5ARK=co$@#ejIjJS7Acvs4IR&cNH?_Dp
zF+CN<e;6^C3RUc!pO=@KT!LS58dPyuYEf}!ex6%mPEJx{ayG~j7@=VZOV2KeC5h=p
ziMc37qq^B7(a^}u0+hqjO7oHxG>|fxrb2K@QD$DchNeO>n4wV3#aUdQS(2QgkP2ca
zCl*7MLvxuG7bj;?YDsBPo`RBxalD^@ylYU9e~=OvCqgN<Jcg#v-PO-E$k8W0Ajm(&
z-`U>>izz6X4^6wLUx;gvpCdMHki6uKoDI=52Zsc?I{L;t2e~?ic>4Qcu^L<ML^H(M
z$3NKBCEhv6GsM%`(I*~aC`vG(Wm_}@+=3i^UE@RjTq6QpokLt*FtviC6ss=GAV<l_
zXnI^DoLz&1J;Pk%ef%9!?SW=#G!33ESi%IBIkCAa*fk`?)6YE^i#D{Zjb@XtXK=8m
zpL;ya0F*#L%jjrYgIohcU4uj7gIv8Z!w;$zmhEw9b$0Y~cJ=W=3wx+uL@9u#+tbh4
z-#5U=HN-U@>IM{7BbN(kI(=P(gB{&nv4j$G`GBU$+27C4)j0%<CbV(_O<$O6P%tQU
zyE*#!I5|3dqc{?!)Iihd;uzxS9^~i?w=pF(EwMDG1W8$1Zi!xSK~ZL2Nt%XIX<l|-
net90G>{Ce2Pf4{>P*tu_(orbQ%quZ8(@53S<l?O5s^tOz6a$*u

literal 0
HcmV?d00001

diff --git a/vendor/github.com/lucas-clemente/quic-go/http3/frames.go b/vendor/github.com/lucas-clemente/quic-go/http3/frames.go
new file mode 100644
index 0000000000000000000000000000000000000000..af9f28eb9f5b1a02e2e01b5db3231d9ee2ae6950
GIT binary patch
literal 3740
zcmXR&OwLYBPgTe$DJd}K;>ye|$S*2U(BR@!N~$bLEmq><R7x!>$}a-5(sD~c0-5<r
zTwI(=>6s-NrAd0p`MLTzrOAoKy2&}Ixv6<2srrSbnaR58`TCi8C8<Sui8=ZOMfoN9
z$@w`T-GsG)w3j6oW#*MAacOdK>FX=F6(#1ThEx`$Dr6Qblw_nTq=5t!N<bp{X$px7
z9w8wC`o<7pF0e2{OKE0aiJ1u(M7T6BJ1@UH4=n4En3s~1TI5!mm#mOhnwP8r76n<O
zqX71_rh-O6QGRl2adB#jLQ;Ny4p<y22Q?7l60lFx5|dM_Yq_|}5{p1;JTkKqle1G(
z6l_7f{Gwt#ztnOKr3|RBk|q}y$X11d#G>L<ztoBnu=N^63YqzOL8*x;sYN;pSe%PZ
z4CDn(1r4xUz;4Vh(p0GC;w&suu(DM^geS-a5TiAUG`Tp_@{2&KI7`5K(3FDIX%vE#
zab~80e4wCctB{wO1J(f&!sa$)C7dvy!cE7bL&E?ptI5U5$q6zvwWtWi9!}1p)RNMo
zJdn*`m0*RnU@|jJ0cHinQ5cG0Q2<sB)&wRnjRXnn>nk`X=H!6W7Z#T)fI<r#!o>=C
z`6UV|scD&csVNGXd0-vT6j)r4n#=|ENr{4;f<Z-!f|?q(7(?+S%okt}f}#P7^@zl&
zQKG2>3J!3XVTl@SAqMgvD0pEuLBk6zAdrI#tV3U4!7~l)JuEI#NXbmeQ!i0~W)ZM5
zaQNsbWR`$3YJO>vLQ!gQL4ICwW>RKOW=W+&NxnjHc4mP>W{DmbCnv}bXr4k%rufqy
znybMEadC1MmuHqFXDF0_{g#|qoT^|@VPFM{dZ-4ql*E!ma3-tvNzF?y$*@w$sRaim
z7bhoFgCUBB3~)9qCZNXzSr0fj2d9>lWagzqG-?#-DCB@53u=Y2l>#WKIr=%f`osr>
z274fEHw8<9#Nq>j{Cz!xT@iBTU^#bx$8g6;glGy_)YmZrY*M_Z3pg}5Ira4+0i9o#
zS_Dgfkm9izl04(#i7hi<&pE%K(oZ8ZU(Y48I61K>MMt5qNJjx&ZffLcYFcAte`tv2
zWkRA4VreZ`EwoUC2ed+QNl|GsI7%Qvt^h5KYoUdoMw)^aT(zb`cu{6asz#E67P$7&
zb1F?sONAC?rJ2c)N<j~*NCy<Znp~Vn#T#5EO%Gz7CfrHLQAw1mkTsC*FhgP-mYkng
zT%u46Nh~gjC5h=piMa~43I-KvX(>kL=FrfDDuARWB)7wLfT|`gPXCgO)FK511%=$i
zf@nxE$3iGjdIObgFoP6IG7?J^%2O3m@<ACmwW1&=GdZ&)r&6IPH90>$FS9BY6mE#x
z2`xP%*HAgo7}ZqJ(1Mu{&M)Bf1+9lN(-d+P>=Z1tGz@KQ3=P0_6G}0amRq9d3TkJh
zX(*NErB)QACYPk9C=_Q_rGm@dVAqfkPe1oyND{VEP)$*SWU5*&&ZN>bP??#Vn4PK-
z9Scf05UI>G1(ZY%DpcG`b8<lWF{v~SGgU*&Qd?V)|6GfTLg5Z^^>+i8DkyHw%-3`E
zcY`E-6p7TLA}&zb1}+RiPE$h(m}-!lLBb%Xf*S=Om!%eIfE>le32HqQr6#7J#V)A!
zNdl#64NXwR0(MSj3OJBKW+7z)kZBr8paKW#Ay}mhN(RLl`K382;0i1wv7jI|PfsB{
zRUt7aCqFq66yl)9Re45!4y=ilm;-W4C8)H<^drcVoMnlSGGHKGfhhemQ@|kuE^48n
z4hu+dDV>?70P+w_46GQn*?^oBQ%XTOBe5hEmPw&0FEa%cykPHhq8MkZP*PNy3aWE5
z(-g`Qb3oO+0jLQ8Wg8N(Ff*?#F()$xq_q^1@;!{>T^vIk-Gdx`p_Z2=f@>3q?cg+~
z2ltLGNT-6Wt%4yJCud4(T4HHV3C!Q|ItuyOpjd+F0T&n1nJKZ>AmM6|n^9tz)Nsg5
zgJe!Uu)!cVz#EI4NH!|iDu8n;Mj?d=Q6$wM_bGrq1<7Z%Trf9+6+tR*<hleJ=a90J
z^kUmY6OsXQK&={ZmqpL1vLw|xzcjA|(q0DTIZ$L;*(wwz=B1}9q(MCl3c(x&ZItG=
zPime<W{ReQHgX#rBnXMaS}stVEDaHTxXgh13Kq>+3<T9Twb<)INF4yG$FW(5RB*s+
fPju_JII&i;P&+{-6W&n4W(6b%VUvLcO)VDyx3HG`

literal 0
HcmV?d00001

diff --git a/vendor/github.com/lucas-clemente/quic-go/http3/gzip_reader.go b/vendor/github.com/lucas-clemente/quic-go/http3/gzip_reader.go
new file mode 100644
index 0000000000000000000000000000000000000000..01983ac77b8c79eb430d13f89ea40bba3b29a89c
GIT binary patch
literal 798
zcmXR&OwLYBPgTe$DJd}K;?mbwNX{?FOifWpE6UGR$V)BJFDXjQD=x?{D$z^N2P;Ug
z$}9*<O-xBGQYbG<EGSk;R47U<F38U-PE|<CPpMQW&R57RQAkeAQ^-lI%FL+*o0^!D
zqX5#W=a*Uz)t#TGP?C|Vkd|3g3{swxqfnBs0FvX%%mrDfpuxqdl$@ViP?TC+tPj$x
z#KozUnXkm933f;_NoJN*7Nmk*0J0&qNTIl-s5H4mp_+>m?C8vVJ&=TRPJVG}k%GRy
zLTO$~YEe#QW?s5N5Y&5mPWdU7T%1)!3JMBZAoqZ^q!uZF)I<ELo0*qcl9`y3S(Tas
zwoRc398_GKRjEZq3aLd!`5@g2V4$zBP+XFkoLvbK=c?u6N-NDvR>(^&cZUbE2Bz~g
z6;NGU&Ba-iT2fk+r=SLM704f{Mb#jER^S+^g`2FAUIlSEM2#jmxHSqCqGOXPOHwrz
zH1ZTO^Gb9;?g8QaB27>LWu_^lSLuO0plGX*mze_+gjs2z1C!+9tmWc_sx4BmMW_U6
zE`k_p3l0=SBG5>$(gQhElZz9i6%<BDR&auJg2Ze=uHoY3M6xussECUbWMeJd_aN0p
h;5gAJ(8TR$a2#uBf{e^B0(k`*cu=S4LFKt>xd4<#1keBg

literal 0
HcmV?d00001

diff --git a/vendor/github.com/lucas-clemente/quic-go/http3/http_stream.go b/vendor/github.com/lucas-clemente/quic-go/http3/http_stream.go
new file mode 100644
index 0000000000000000000000000000000000000000..4c69068cdbb5c815822fdcc531f5225b5e26026c
GIT binary patch
literal 1706
zcmXR&OwLYBPgTe$DJd}K;>ye|$S*2U(BR@!N~$bLEmq><R7%S&QR3p_R7%e*$tX?I
zOU}>L&nZn#EY?lVNzF~oD@oNaEX_>TP0v^2(&XaO*H>^<2rel~P0UruELKQV@CXSB
z&^J~n1`Fwd<ij&k^AyU9GD|Y^(iKYb6%zAO6pB(4Q$XCbqWoNil8jWSdL4z7#F9ji
zL1{&axv41%nRze+T^vIk6~Lm!dR!%y1*tGwL9Wq*xB%>&5V&ay$@zI{`9-<KAX`C}
zASqSI%qvMPN=r;m)lo<)Em6qKD=tY*Oi{>B!{yYJ%%arflAKDAm*CD04E1z|yFIf6
ztTz>GAIz7<3UI}spn)oedI1tjAhFWq5`}6mP9zs{adGD7xq*VgwWuh+NC6JgO7oI6
zG`TpzF&31Xo0yrGnV0SfQL9jznO9<F!d1(~RhC$!5DyC_TLm?UW!1G@Tp$w^@>0to
zp{7w>Qlx-nv5o?Yy&%OJnwkn)5d9!m7NwSy7UjWBgcxH5@&>A2D-@GzL5>2Mrl3&_
zGfh(=C^a!fBS|4T7UUgG1&z$S5*>w9kdHM%R%NCs6zhSciZOg@YpY-YQc{puRGjLQ
zpI>0b#mSkLUj!24<OC-but^G5whCZ1eyJ5D-~iPqh9(#tg}lrhO)gGO&dfBBr3#9+
zAQ6ZOoKSBXfK34@ss$0n<(VbP84772W5Jf_X@HU=*tMLT$%)0O3R)Sdi7BZ?#bC=o
zuHxj>*H<Xc&MZ*yaCLNX4GM;)WRQ8B$@zIDnR%tDAPz)3C=(#*#Tft!X?i}XdFdq?
zAg!E9MX8C|5TAlV10<G`nwD6aQvz{9u^w`~f>IDC*j=b$19oPyLVj6lkwR%+c3ysY
z9>lQ<AYT@POa*x+BqOybH7&m=RY##DBeNKk)lxD`GV}95nKwT#r&0k_R1~GAD3pV<
ze=5XbDGG^s`6U^tMGEL9>w!&11bJF+i5@uNr)ek^q^1@r6sP8uC?w`7l;)*Y6r?7X
zq^3aJWu>4RqNJmc298Eh4ApXR)^c%if|7q8D4Ro=sYOMg7|t)^0>w41Ahm(!AB~*U
zJdGqxNTlY06M%xP0xa3+K@x4W6%J!!HMuxz6;gAGQ^6?-mwu2cE>0X~DCpY4+>)os
z1xt$%qp)VA@S@C;R6IE;sT7=K)xeoa&#5#mEw!k+mWxwE4W8VpA*sm<W-&AnYBlws
zhL?guIWtWm9ugu}NI?Quq2~muNi;MytwG_9oTCttn_5%^iX@n?;Nc22M3bwQ3jlIU
BBk=$L

literal 0
HcmV?d00001

diff --git a/vendor/github.com/lucas-clemente/quic-go/http3/request.go b/vendor/github.com/lucas-clemente/quic-go/http3/request.go
new file mode 100644
index 0000000000000000000000000000000000000000..0b9a7278cc0a4442057190304a5fec6f3456465a
GIT binary patch
literal 2599
zcmXR&OwLYBPgTe$DJd}K;>ye|$S*2U(BR@!N-nA_D9P6^$thOi;#5j4D#|Ycv+`0)
z^g*gYq9Ar@Q4WY%TvC*rpH~KDX6B_AD{*mgDy3(ZWRxc9CFker=Oz}Fq~_@sr>5p6
z=H=-Zf~;2J(&XYwE6qz*C`v6XO)V~QE6UIHNKH&hEh^T?fY1ujv0$}&5OKH6)SMJe
z1r05bBlLnG+H@4a4%Aer=He_%EK(>)EXmMONGvVM$S=w)snk)(O)bgDPtj2*D9SI%
zPtMQLQ7BH%NX<>vQAp0uD@n~O@kz}~FUbflDN-mdDay=C=i=f7Spc!KSi#B`97Iqz
zRM&EGrsWqY#Oo+zfTW5N^FUz;b#FBnCuebaW=V2}LWZ7SVs0wPRh-F*#i<HPRv?!t
zS#fc4a)MY2wh9?~VTn1Vsa%{e6%eOF)IiwSRKfiQQ3&T^Qw#GnL@A7iO(i5yAnG7&
zEUKU(rkeu}GKgZR7$OvrJ(iN1mROoo0`_%gnu216o@a4DacXHwz6LnPI60BSTF)^h
zMFSFWI<U~u1O-?v7bj;e7iTRO7pJ~HDBhA2OH%U^OHvh*^YgPaQx%{IMn|DI6_TQh
zt@QOv^7C_w^)gdS()99+()BY+a&z>H(vr=L%uJPwQ<F<F^Ye5~^-Q=pGt(4uQu8#B
zVm(^P8EmjptfqpUf&n-hk)-v4Q%f{pN_3#<rdZD_KNE*3ItohG3QC%q5I<xVJLl)+
zr6!kvQV=Ynp~=h_oLr&uVDGwCl%(dRq^2l1`}_I1I)|WoG9|Gjv7{(5IXkro;nkG<
zWVm-NOiT>n-UWHK7-k^UlGGHCGZ1c4Q-kLf1w~s0B_(hGWn$3=O759y(EOxe3sS34
zQv=R|2p&@U1S<e(;w(xnDJ{xV$ji)u=HFsHztnOKrBpP(S}9mT5~Gd+IR7Xl=A|fD
zA&kr|EiO^W%P&z#N>xbBEhwo}0;LyFQc+0FDNa?$OjAI33}ka=8pL^EkAWPEFbm@T
zY6X3Lg}nTt+{7GMfP+#XE(b$g1$G9^31qktl=?u0f+i@~3_#8TiNKwPDhx?5P%)67
z@VZS0Ik>=~2hpzpb~Ad=fpQqA6fITIDlN*<3k~vtl&a7&IyA@=n%E)2sYOMQ;+G4W
zMxaRwoL@>6Y!%d?rhtk~P&UJria1Nb=|I6&0i+=yv8Xr|TE2${d1`=z1{4dKX&~!B
zsWLA!2W$x^dQt)@0~Z&N1ON&j&QiUA#F7jJTX57viW1IJy<l)PpkND4zg(Q0rFtIu
z#U%>1i0Xq2WInih0(l)pq84ERQn{++5fT!hZ>(pa1Tv2pPhs&BT7*Jfj1&hjpTMFE
zr3_Zc%quYiWpqfXh9VbSQiLebP-I|Eh1TYJ;5hKiE73qv6<kuJqhM&DqhMwN%4o#8
z9pp}^r_?~jD6H}Z#SWyvvr>QpNb1z#;^Yhs@_~ygC@7SI!~#H-I6@Q_@Ng;L#H{=x
zu)HxsEHe)(W&jd{)azh1AS*%6fe00YWS#O;D&cYp-~x(^lhYZwrC<fBfDjD_kS<WN
zLDB`!xF9)1ItHsi#3D#C#3vXm3UY~pf`VE}PO%;|4`t@(1(zh2q*mAJaB<e^fSkfr
z3vDuG<QJFZCFZ7r+DuUMHHuOT6;N76n$WBY$|sp=pk@{*KO<)zXp|JC7J_8Ji62x!
M!LpQ+5?3u305JO|%m4rY

literal 0
HcmV?d00001

diff --git a/vendor/github.com/lucas-clemente/quic-go/http3/request_writer.go b/vendor/github.com/lucas-clemente/quic-go/http3/request_writer.go
new file mode 100644
index 0000000000000000000000000000000000000000..3dd1cbaef341fb716b2d3e38b998ed82fe479869
GIT binary patch
literal 8116
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
Q7vg<r7XwKg?%P@}04ldXtpET3

literal 0
HcmV?d00001

diff --git a/vendor/github.com/lucas-clemente/quic-go/http3/response_writer.go b/vendor/github.com/lucas-clemente/quic-go/http3/response_writer.go
new file mode 100644
index 0000000000000000000000000000000000000000..70a7cd3f484382167a8b7acead1d2cbe237099b3
GIT binary patch
literal 2712
zcmXR&OwLYBPgTe$DJd}K;>ye|$S*2U(BR@!N-9mu%vS<2Doav}L5#fA5`B<55WBdf
zC^<i`49d*ROD|U9;^I_F&n(F(P0~xw&(+T<O-?M<P0mTpP0cGw)h{f~Ox8^Y8%R)*
zer8@tYEfQdj(%xLW)9eHBt5x_MJ1_uy2Yufxrups`h_4bC~;|Wag|gSq$(7p78m5_
z6{m(5WtOBCDHN9!l_r-cRC94A=jY`qKmo`xdd~TId8x@InfZBKoJpl=X{kl2DZwR0
z3R+;F>p=|Q;^NFmO-xBGg6aeZl%5Azh>NqhB(bEl7$%jOSE8V=uK*QH&QD2IC`c?W
zPEAoL$yWdy2h#^J5+qcTnx~MIpP$3U#hH_zo}OBy0QRz;517MM%f(feSOkiXc!<qG
zsNS<xP($@_buAYsRH<7|X>kU^;W(6fWM(BMXCst>JcCWCCKp#)X<o8IUTQh28#Ibb
ziWI;B8eCG8nwYDj01j!e2qK7e6tH+tQ$Y*W^`LkvN-ZfZ%ERy%7bi3#tUxYQfP@Al
zJgaMUxH!S_3zbs<I{_q!6t7kaka+b=Ee8dBY7xlcnjl3GmqGPHI67RMwV=QUds(9#
z-JO~W5SukLk==ph`f@!;6yr4m><p+u&_t7&SE30rCNoU|W&}tTD3w%madJY!2IMv_
z&dfAeO0!WgG%!%8se$qBY!!?Q3_!+kq8nnXP*PNy3NnDRTn}oeEmR*D7bhsKK^$JJ
zkOa#1dQRYs!Nr-Hm#koAs{qbydVZ<pu6dyJU8JD~RjvtE0?9OPnW;G`8W5!r?}0_C
z{StFitrV23AoeQhD1;^El%`sN@+hb*(eo_HPt<@&YSwCUadD>Q7b#@xD3pP0E=tTx
zPlX2x*mEH1%)FG;3WR(aSOVl)Vw_T3QUofw^g{A|^2<|;G_p0}ZYqlgn;cuK$;HW8
z3kooBApx>U4LM{$`3)WjZbgZ?sntHIdFdq?Rtlw=c_n5h8qfgJ^GVIq(A2Eegg8ti
zsWeTKi?du0lHT+@^V0IuG?XA&D<v~8U7<X)Btt<pMM(!5J(}PskJnL1Eh+*zqg)Ru
zaU%@Xa{|SihNh-9SfQe=LSAMLI8@+vxfT`W7o}+^CFhssq$uR&mnf8j3QGm3OBK>U
z-mp?oEmi_srUzE9p{WT91yHiX?*^zT6uSfB426Qk%AEYf6g<vRL{H?PoKmg_F8(yY
zj;@84UDz@*D0`)96evW;f}&MZK_fG-1nlknB3MquVF6etBz<cb85n?V0tJs^QhrLM
zV@^(fd1{JVeo-*Ej?jQ*H?X15jI3aw11UdTi;A4`Q!4%POQ8C|L7kEYif6Ty#F9j;
zDJv&6PoqFnvle&COi4o|42shar0@Z`Ikl(=>_Dgpwv<?)iMQy3Mh+-lW~PA?s+Fw*
zsyR?4q$J}sP>lzwY>UA)5soy2FEv1_Fz2Gw#FG3X4NV0UKFGCjFM}#MkQep!6_WD{
zGE-9&(u(qP!O>|1ZY3G%rRQ_$>nmVO4GKl6pmtWVLU~4N38;=vR7lS(OU;9~UZ8ni
z0aVC>3@J!0%FQe(R!9UJr>78{nyL`w=B!|3W@x6PPz-6tD46J(=s{{}Y?eZcRt0b!
z18Q@C{8L<>S(2OqVkIXQr^2kYvjtZvYHBb68(RfKOG_(w6{L`sm{Xj}#fhxR7F6w+
U5L9DqfJIF$7c5JF>!(^S04B(HoB#j-

literal 0
HcmV?d00001

diff --git a/vendor/github.com/lucas-clemente/quic-go/http3/roundtrip.go b/vendor/github.com/lucas-clemente/quic-go/http3/roundtrip.go
new file mode 100644
index 0000000000000000000000000000000000000000..5cde95a62fd8a6ffeee9bc34fbf248f09d1c7b15
GIT binary patch
literal 7166
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
V2_Ravp2c7*f=csJHHtvhKLCP*1%Chl

literal 0
HcmV?d00001

diff --git a/vendor/github.com/lucas-clemente/quic-go/http3/server.go b/vendor/github.com/lucas-clemente/quic-go/http3/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..086bc8e256452aa23187438c9785ef36bd5b33cc
GIT binary patch
literal 22266
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
CvcrP_

literal 0
HcmV?d00001

diff --git a/vendor/github.com/marten-seemann/qpack/.codecov.yml b/vendor/github.com/marten-seemann/qpack/.codecov.yml
new file mode 100644
index 0000000000000000000000000000000000000000..00064af33101737796f3746de1a6b8059bab0b3f
GIT binary patch
literal 103
zcmYe!FH0>-Oi#7qQcx($FU?D_Qpih9EJ`gd;Zjg2E=epYEe1&`C=?XsXQd{WKsX8t
sDXD3Rr8#h}f<j3~QEG8Ueol&&f+3fJf`URpVo7p_l|ou#PH`$10CGhhd;kCd

literal 0
HcmV?d00001

diff --git a/vendor/github.com/marten-seemann/qpack/.gitignore b/vendor/github.com/marten-seemann/qpack/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..66c189a0904978caf9f02955729eb557c2856656
GIT binary patch
literal 110
zcmYc*t*Xk*OV`)ZtI90kg7K2`%TkL9it^Jkb5aq4MTx~3sYS(bf#UqUL=08Mr3D2=
Usl~;a`FRMH$@xVErN#PO081At*Z=?k

literal 0
HcmV?d00001

diff --git a/vendor/github.com/marten-seemann/qpack/.gitmodules b/vendor/github.com/marten-seemann/qpack/.gitmodules
new file mode 100644
index 0000000000000000000000000000000000000000..5ac16f084afde78701f1ae3b0c1c963ff2150fca
GIT binary patch
literal 126
zcmazpE=|hKPbtkwRZz;zD@jc+N-W9D&nrnSE-BUr2^8fQ=oe<D6)VMZaTX+&WGL7w
zfP`_X<KiqW%2BXY$S5f(D7MnqPtPpLC{5B!&d=2^EJ#ewPAw`1o2QqaS;7SXzaB1u

literal 0
HcmV?d00001

diff --git a/vendor/github.com/marten-seemann/qpack/.golangci.yml b/vendor/github.com/marten-seemann/qpack/.golangci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4a91adc77ace30b4359b17c42ff01ef5bd2d2e20
GIT binary patch
literal 447
zcmXRa&9mal$;>NBEh^S6PAw_P%u6qZi&$|fD5PW-Cnn{j>L%voSSgegm8NnjD5U0r
zgg~+i3c3o3#mSkO$r-81*<1<=Ac2(B#FXUx6p%^~H?<-ov9!1(vkW0nkY7}ilb>Hu
zl$r+Blb)ZPpI2N0=cVPAC@3owrKXhTr6lH+=qMCt<|U^pr00Vqw2CwGOLJ1R6p~UE
z5*70Db@K}p64Odji$L<FxdlkZpztztK^7OoEh)~-Ey#hnDl;!NEiJLQI5RyDsvtMB
zxVRuSCkM(cC`wJt$;nTKa*LDm3sONr2jiC{mSiR)heL5mWlk!pKv8LO39?9OUUGh3
kS!xl?#ie-#iA9OIP$Nt8N{drdpq#SAB4nLqsU=)o0BfFxWdHyG

literal 0
HcmV?d00001

diff --git a/vendor/github.com/marten-seemann/qpack/LICENSE.md b/vendor/github.com/marten-seemann/qpack/LICENSE.md
new file mode 100644
index 0000000000000000000000000000000000000000..1ac5a2d9ae6a99a7da8cf29193770a0d8c04e430
GIT binary patch
literal 1054
zcmZ?HFQ_caOwTA$FfuT-RPaqKDoM>#2u@ASP0Y*V;tEJD%FQe;&dkqK$ShXKNG(cD
zs#HiXO3W)sP0>+ED@sjO$WK#9&PXguPt{Q<$yZ3st5hgREh^5>Q^-#$NzBa4%u82D
zR7lP*s067j$;d2LD9%qSDNig)RY=TBQAjK<&QH!vEJ;mKNXbtw%}vcKNh|@`mzJ56
zTCAW^l98&Q6b#j&qzSepB{eZeAu~?_BnOjHD9<d($S*BXC`v6ZDauR+nW3YQnU|bX
zngVhIOgbksHxp_DNITf?#UPiL7N_cf-KnFHo1c=IR;i<qn+o<;L1|J>W^sm&LP{pc
z=%mt;R2_w4kYI9Z9!OVWUW$HxkwS55P7cU~%+z9tUsDxeuG0bgwIm-DHo2K4AgVY&
z4{UFFMt&};Pcn-Y(n^c+GK(`(Q$Q|D$yX@O2iusHnp^@B0l7RaKPM-@9OSj+{JfM*
zkbjG<xVS<xQWX-D^2<^|E`dgEUVcetG9<Xc5m11LRj5>PMq*BmLQ*O;z*18{!3z>f
z&MyFarwHWX;*!L?lFY;$g@XJduzl#>)Z^j`@o-fL_IC>jcMNh>@C;T62=Wi}ba8c2
zP;v}b@C;VcQ3&@8@$e4~Q3!Vo3Uc%diB#}+Q*iW)RPgrnbJ0<7jR**G4GvcD4^r^-
z4e;@Fb<t7q^mFzJb@B9bS8xgqQSkE*QSkBf^$c-!Q3&x@09gn%%F{I%WSFmOkh6!Q
zUx=fVr;leyq>h4{XNVujOgH}^1xJMd$Dj~T=TINVAccU?paB11R|Q8u7X?3mKTkim
zAWuJcS6^4Z5IqG?KLtO31=lcFzYv9B4@Vy#uvLzsAs+rgVD~xu2Sf&Wx_g8uc=-Fc
zxCR9)IJqkLcse@yxGI3H@{3e(_Hp#|)lqP9^mTLxxh_b-Kg7c|2&@e1x^NFyu#l&p
zf}@{;qjQL-zaPjO&i;NOL5|KLItn5FK_Q?30c#ES40hE~a18Pc1_g*)kiV~v0w_5B
z-9Sn_{S=)2{rp@ZrhtMP>}q(7_y>Xcp~0>o)nEe^TwEP}JpJ5*K|ThBCQPv&7XbS%
BQHcNm

literal 0
HcmV?d00001

diff --git a/vendor/github.com/marten-seemann/qpack/README.md b/vendor/github.com/marten-seemann/qpack/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..a621825ac9d5b5d7f5162d7a62c739a63f1a1b56
GIT binary patch
literal 1203
zcmY#Z2n=v^_U7V>R*ZJfPsvYK2ue*$ElSNxPL0*bC@Co@w$j(n%uUxT&d5y7Nh#LL
z%-2s!Oi54GPY0>iErO}mP0A@v)hjMbw=XWK%t^IP%SkNJEiNogEK1cxm;~0OmtT~w
zpPpHgQJSQeoS&<on^;tmnx|Wwnwp!Km#1G?keHmU338BgW>IoZs<WqpQ)y;Sib8Nn
zVo7N+!Z0*vC4*HbXX+Oe<!7ZPmq4w+YCLv_s^ugmrRLbetkq5eS&QU)m<?c8r)OX_
z8RGE#lvD-h{Ib-d#B|)@n4F)Inw(#zpN!9?`nie4C8<T&9gOTYh+&}k!Ra!tkc`Y?
zh0J1wM1|bUyv*Fh97yadWabv+q~@mPl_ZvA=I1G7<|(-6>nV7afYicNX67lBWTYw-
zr{)!>DwJd-mMCPFD5T`47U!v#D3pT5QWa7v^AdA2lNCx5lX6lO5=#^kb8>VPl1fVy
zvPz3fAO?Uefvd<%QPA)xO-sv7%u`6s1BFP6rb2N^QD$DcLQZB$YEfcNv7SPBMru)N
zS!$6&L4I*@W>QY74#@JN)Y9VAVg-;Ug|wplT(I%c9-#D$#YZ5=yXPwumn7z;Bo?J8
z<YXolB^Fg8k`q#jOwZ3r%uCnLOD)kaDN0R+#(_R4KN#s}fYYZQ7nf&=LUBfZX-<kl
zQmO*j!zK9&nRz9tMfnA(MTsS;3gww484CF&8L3660SyX*{JdfXjimgN45+`0iWL&`
zQWQW*A|<t`SW`zKBflK%>C6&^@_bMzCnjfPrk15DBr0TNre`Q5=jRp_r4|>1qAWEn
zEi*YYH7~hRkBduLSs|!2FAo%kAYVhBq)?JtTvE)%<(XGpl9-bN2`zB$hdEjyEi)$-
zDT5(}2sl%w78UCkW~LSEYbqpFDilGi;7UkHNJ=cu;7ZRdQ7A4=%FRzH%}G@#El2@{
zfUa(4US^4cu5M9ka%oX<W?3poJr`F<Mrt0&1h6Z>4zmJDKn=>vPS01+EdskMy$GBr
J!D>KCxBz6%uW$eW

literal 0
HcmV?d00001

diff --git a/vendor/github.com/marten-seemann/qpack/decoder.go b/vendor/github.com/marten-seemann/qpack/decoder.go
new file mode 100644
index 0000000000000000000000000000000000000000..c9001941333ebe646960641122a87fb408179fb0
GIT binary patch
literal 6190
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

literal 0
HcmV?d00001

diff --git a/vendor/github.com/marten-seemann/qpack/encoder.go b/vendor/github.com/marten-seemann/qpack/encoder.go
new file mode 100644
index 0000000000000000000000000000000000000000..13e0ad2c7d1f9631e8fef48c655b4da24acc4a08
GIT binary patch
literal 2616
zcmXR&OwLYBPgN)cF}S!ga|`l|N)$A>IF&N<mAJS#mD2Nb67$ma@{7{-EA;bHOY}2J
zN(zkhGeGK<xHP%A^z{`S^Aue3lJiqiixdh{i_-Foa*GuL100>b6;eUMnR)4YTqTtS
zsc==rB}JvlB?{GCoaIINC8+^LscD%N3Q76-Ib2+v<q8T4nfZF*MVTe3MO>UorD+P$
zu}PIBsa&;S+x$|?VYU^emXsFd6)Pkv<fWFwO)t;LOwPdR5(URRkek6SOHENoNi0cJ
z$ShVUFUl+_NzGFz$yX@X<4P;dOIAQwq*0D=k*0zc%=&6BPKXN>)SyDu<yH#iwNQtG
zec+avnv((vhty()G=<E(l6-|kh2qS-^qf=$uo94+sp`cFrFki-MLCt3dFc@GB0Z2p
zLozap6+rGO$;{7F$W5$NNX#kDS12gTPbp1KRR9G{u|isYkwQsEs)9#qA}AJ|a`Kb2
z6(GS1G9xohAulyKwYWI3s8UbCGYzIYMMojC1myda{Jd0!q|~(hqEv`mL5@g+hJ!{b
z+{>B}mw`Q{kp{I6%+pjzEh@?{0>!DmJ~%W}ak_{zGfhD;RS!AttGPHiQ}sY`ZL5%2
zP>`CJ5|&uxnOC9#7SvI&&`~hZB%sC|t_Im;TZNLM(o`<aS}rcm%#;d8kjXj4ItqD-
zxv6gXrFkg|R<;m7rxy7p7DT7%`6cG2#&UriiBJP_UuK#D!T`Op#GKO9Vg*}Ug}lrh
zkheKOI&xC;G}83KKysQOIRl6cC&;&%C8?fyDXA5yDc}&(Kr$>dr2^zT&RT`koZ?he
zJwBj(keCD36P{U;0dihYYFcVhYF=`xhFY2qva8?*adDy;R+gCKnNp#n0CqLp+ej_|
zOB6>#-5$%u3HA<H6rvmy0-!hoWfW9@K+L9r`?-+401ZL((8w<>K@E^JO-Nj)>Xk#v
z5J(cz<btO!aFU9)GKhs|-@MEmXr6V>$uCY-NJ&l3Ni0e&Rw&6xECDBDaB%_36q&^e
z$@#ejIjJS73W<3s3Pq{KsU;wV@X`&G;Y#uql2R3lQcH_dQxp=@6EpJ^Kv@)0PGvwc
zX%Z-tV#{J+H)tSbD3I3?#iXr5T4GLdD!SuAMT7!MRt3it;qXGu#noJ#`DtmzsU@In
z0ZPr_&_W75Y`N1|M*&<R=y{Z;rR65(K}tiP)V%bP3=K$z(u5fi4KY4eA<kC8pu)&N
zAx^=d!U7RH5Q7}SWdPK;;F6-uymUxB>L@_XgS*pKfmmmn<98+`*J5|4g#k$}gct=)
zQ;^UA)l$%yPXpHp<r$!)1}b7eC2#>KA?1}QWacS=%HsSqaIz~&Ov*_u#+FF1MlM=T
zP|$+Z2jHRuR7@z8X6BVxXoAWYNGy;N$tF5r?Pew#nJJ*y)Yn%CPKD$S14F-%ii(N~
zg@VMAlGLI+9Z+gd24xpNP~~O-&M6@vrXe&KQB#4b!4QniJcUe1X|4c{PaTEa)WkfH
zpEFC;ixpDyN-~R5b1D^5QqwZ?Qd3BaShRWq9Ps4En;GtSGX%#Pws^x6S;+=mwOjyy
C0#`Z!

literal 0
HcmV?d00001

diff --git a/vendor/github.com/marten-seemann/qpack/header_field.go b/vendor/github.com/marten-seemann/qpack/header_field.go
new file mode 100644
index 0000000000000000000000000000000000000000..4c043a9928c5a45d38de71bdb83acd9f7687ec9d
GIT binary patch
literal 477
zcmXR&OwLYBPgN)cF}S$&^%WcyJW>-=Qj6R&Q*%-jGK&=w74i~uQ+3M{b4pVc3KBDm
z^c0-(OEMHnGE%`Z3W<3s3J}@EqEwI(B}J);C8;S2iNy-}1&M{FsS3rZg{7%^$*IK(
z`DqII$t9^J#d=&Nl?ACNmKB#2l_r-cRC96qCFZ6oC={0zW#*-GafX4dhVp8`uJkMp
zC{8U+$yX>!Eyyn_DOM=YNG-`oEdskL1MC5XG`J7)6g)yg0*n<3AbKG3dLXBUWF(d-
zWESfvWR_qUm06-tT#^VjJhLQ2AyFYYKPNvAtj!aoNg*%4L?It!Yk6jIszQ2cVo_pV
zNos0}LP@?tQmR5CC{!|2P;G^{N(W?jNk)EYdWIfXT4`Rgf<{Ifa#(08z<j8osgRVP
xp92b&qSTVoqCAD1)I5!hG(AulX(}k%Di|oJsVP7Oq77mdY;6_Pt<<?{xd7QBpJf05

literal 0
HcmV?d00001

diff --git a/vendor/github.com/marten-seemann/qpack/static_table.go b/vendor/github.com/marten-seemann/qpack/static_table.go
new file mode 100644
index 0000000000000000000000000000000000000000..930e83c7c12c392c5d3c882b2a0231bcd021bfa6
GIT binary patch
literal 9113
zcmXR&OwLYBPgN)cF}S$O5{ndyOA<>mlS2}da#CIMN{TX5ixq4YqV@FjVm(q5Q&NlE
zGE;L>s<}9;{StFitrV235=%=m@{2M{DwS$=kc0~oOEQ#n6v7g7N<qr>QDhU-Q<3Bh
zP~?*H^GZ_lN_0~)iwp9LGfOh_^RUY2q~@igSb=U#es*Rmiq4e85>$cIlEicr6`5(e
zx%nxXX_=`hy2Y7!$*9UR({%Il^HOzl6HAgaQ1s;_7MH+vqRM3EWupq@CnsX~sVFrq
zwJ5a+MPYGjiEeT}h9|6YQ%f@PQ;>qs+27C4)j0%3Go~sRS0C39R|1OMT?rZE;p*r@
zz!3j{5Kn)<U;?TF{DTSE6%dN1qBuDtH5WPMWR#Q?pxT2~Nimw5lEjkIVx$l>G%zNh
z!pHzMby<P335prU1||f|Fd?YI6wMup$;qh&B}fj{($_+<1Vce$K|xL?B!TIt<Q41Y
zrWO~2G8=N%glN)D%}dTt$;?YfGCsX3vp`27B{eN4u_RSTAqh1>LiH6T=B1}1hh9=;
zNh)dxgH#q5>w<D}QGSkYVopwexo!ry_(0Z}oS2-E3R8vQFJcUU78NCx1*ka{s{`_j
zGSf5jki3f;mZ+{llw7%q6}pM(skW#IfRJh<Q%fTQv_wNlkC}mq1(90v@^!(cp@bS(
z1RkCUALQlh7MJ7~p#~8y#Ra8FIhn~i3NUXQ8=4xMVfX|Z@L19iTA~4^0Z?g<MI|V;
zp}HSxDmXe3&L<{K!>qxhH7l_!u{gOXv!H}jOS6h`R6%%btI#bkFV{`WFUr*|Ey@A=
zJ~f3Hi!*Z*(^K`+Gt-DyoK=vDT8-fd4T#YNc|<8LNv$Z+PcBBy*f<OZtIa6M&9PQU
z&PXgOPA#!5ElJb0AjS(|y#+amnb>Q2v>-;Pw#ID}xP(H==ima$)<74<ImIPKnaL%(
zB}IvO#Rd6ACA!6_$)(_C3?k*>%>(2ZV6Bjumz-0Yl3HAvl#-vDn3;zfzEm>PTA`pQ
zH77q2HNll77F8mJ3aE@MC_z*M$W;WIQfSGEtfB&3&VX_@D28<N3qWle<ib5Kzc?>5
z4K)>4=vEXL>lPH{m!u|x)FZjZ&{`okKPA;RDJMS}wZVj0)f%D|l9(!t2v)U524*P9
z0n-d)10w<|ObEKe1g%2Hw8g~8kdQ4#rUX=&qPYVavpI=*>802ze~g+nxhORyHLoNy
zF$XyY-5h;_Q5%dPgNf7}5)?{=Ud%QDYMqSLLP!G`*`?{JsAW3w+H@2O@{3D!6kzEW
zIf)Z#3K3dUD+=<9Q?a#xpfw(}FN4SSqSV6D)Z!96W`Ob`N<u+%Kg<M3@F8VA0-=Xf
zA1LHdEzBv=EiOw&GCDaYH4&{53F$3XVfKMwbqlN<)-A}-$xKEru^_d#ZgEkvf;y-p
zrf#i}pOlrFT!JE$lvtdqTUwL}RfFQf)Wo8kO5K#il0+m&qGrI<iUN>{$*3tREx)Kd
zu_y(t3eQZ_h15035r?I)EGR7kB>*Hl3qU<S^j>6fYEc<lC$J<lH#09C+Jc6fjHsVb
zGfQbfdQoCZs%~Z;I0#d9p)rS?Q&9a{TAW%0YTM?adbR@8Zh?3g#6k(d3f;7##N1RY
z1%66u9_kPPZl%SExv5ak)ar26a&hVFEBNQ-R4U}9rlzE(D5T{VDU_rZmlW%PWJ-%u
z71Hyqax(L>^AdAY6-x3Iic&!%6J@CiB^jyE5etP9(2xa4LtcJPW?qR^N@`+Ca(+sx
zPHA3gacT-zT4`RgLV9XRFk*m1LsKCdd00cCnv1h2wWPEt4{9j#Fi5mjEXb*#Y^IQz
zmy%lHn3n<`-6&QlE-5NaE&=JzOsP;%P*5n%%qy|r;w%FVeiSR@CKg14YWBSJScn9~
z$BrNgh5R(ICm<!E0?32K3TZ|8xnNOHFEcY4;x&-JOEOY3iy*cuWafdDz`d;yl95@g
zkXft%@~uKrC0G{H@GJu9EiBDU&d#Y+NXyJiQOHbFNK}B@3DN~I3)wnQ$S3BdD3oU;
zmMDNdqEMWXUz(Gmkdz9sq9k7dV!T3T2`JnZLNZc|Qq_wU5*3P5lk@XZ6mn9_Qgc8)
z%uOsP$jnQJL~KSX)aGJ^5|H5;iDjt@#re6ZpjJytYFcJqY6{49u%*Q?e<Hh04?I!@
zvAoDPu|UBV6qK+qMva_mE>7ezDl1U*KybBzjzV5$4k+&`S%HVEkR-uMstrN?Ak@gK
zR?=6pQZTFqYXjA;=$aH1s*SK|Gf=WpFoI}<6?Mo%TviIz#t8SoR6r{Rs2$ZNxC}Od
z84MmTf+`35K|w*G+7w|hcnl0pUO}PS450!%D2A!R9H9bplnkW2+5$xdsDOr!l!0VH
zZmG6Js6ZVtgR3w^iW5+VSAxla^n-D=p&^nfu)#DGWz~j82qmC`7BsYmqC%nC5Gk;c
zM%s{6RU4WhOo5H3St(Q-nqr9rE>2D**f^Y(f}yDnNB}zIW~HEDXa*Jl4X;6LFf<1X
zfJf87B8C=V0od4?m4cxqSO7ds23BEY01*fcfd-0^p$-=(B;Ku{!(LVj)kfG7AILS}
zu`(+K1tTM{IUsH^C`lS4nF1LY1DS#?rGZQV4V76b7@2_00F8=SDHuU~3mO2kQZRz}
z6g1XlrC<c{C1_a7O2G)?Lr~-0O2G_dE2k2uoo=OI1~bh}$x6WtW||R*12YYjdCVZD
zf!fqo3T6<~K+R_>1v5xknHZTWSt*!9!U@zUwo))hgb}DYR8XP>@|-1(7z7Qkfx{Y{
z(vS%whz+=gBCHgQLAF9717Z=PV+G3DSPK%+00`=Eo0WnwtYiTX{Xv=+plDK1s5ZtH
zvr6F3t&)|3F|=#}*^bdhhm`anbE=Jvad;keK*&nL7!pOWk_c4qffAlEB(k(XY@`q}
z2Sppg{}?WSR`;N=z?OfMK;{{nLJUB6Nwu*V4mZO(=%D0*<QNds7?N~gDq!PrNYQHy
zNl!2p(9t&}1q#NHL<S#hLzV;Cp`c(4Nf+=DHcS;JpoGDxgmYNUO2Gt?Mqz;suG&Cj
zvsMb#CfK4KlogYTz;R##$<Ls~q-3RF0*W<gFhV;Q(3q<>!Ih_Rr)r2NL8J*J+L4ta
z4XB}b+XR#pIF(S8f=19#6oJfCP%wc+60%b417}tWCXm>IjA4O>$FLX&F;u|>5^WHr
z;K4DRYE2;V2T=+d8N;bk!4wja;1MX$xEOAg3Z|eK<x~PI#WpO4q}3D>dtjxIQ7|M)
zsDGf8DJW{8rh=8C4S^wPGzG;LBo#n9C{_y9rdabGxcC5#jaey}LbD&JsfvAM3}gzS
ztOy@3Msg0+1IUyqB<5k652g#-02z`&rjSHHgf7Gon3aMlC_O+z13a(=@&>Ld091S;
zkBV6-m_m{Wya0py!VHukAjaYt4YN|HHp5!-!v@E!6wILIJ;u-&%-7Xs*z&&;>Uf!z
zf*GXv*Vk82f(<f(`T{AXkoln06a`RYz9ca_71W|mNv%*YGY0hv%ni9Xm7t!64;Wb~
zRGT9;p+M3Y1v>JW5lB6*Y6UWiWTjvZNxk54Au9z1b9f2`IT@=Buu6-7Kg>;`KEQAy
zWTXRJ>zU(9wxGceSWv@b)*O;J(^E^($A_#G%ppk-HtK_-*Bp`1(0qh80%WD2P;HJa
z2`a&A0C>aS+!B(+&<uq3?cw7=RtnV?23TrIxPj1Gz`_8pagZPbHIgi_C0tOo0}U|?
zNErtn1hP`F02O0UlcA$Pu#l;?z?L+WAVWh|3KqstZ72gnAnzbGzhR*Q9@xk%sf3LU
zfs|n@)<EtgZ?wot!2(o$LEMBiTm<tCJdIRaU`tX;hDufn7G_ZG@S!9O-3rwfNF6G~
z=n^)CsQoHfc@B37j8ScY)V;zitzgO&6sj$eI$5xxDNKt&3bCaDCHUBtm4YR-^n#9D
zVKYRb+7hWK!8V=+(^YMWHKBl7hL+Gof_sR|N}<{kTl)qaA(qhYG17P!%u;x;R9hnT
ulF>)NkX2V(B6XJ0$GAYwFvpS&K?xE(Kn63(5>kvHjf7b#Si*{rS}p*7JfLU*

literal 0
HcmV?d00001

diff --git a/vendor/github.com/marten-seemann/qpack/varint.go b/vendor/github.com/marten-seemann/qpack/varint.go
new file mode 100644
index 0000000000000000000000000000000000000000..28d71122e139c922be0232e48d26da18432366ee
GIT binary patch
literal 1589
zcmXR&OwLYBPgN)cF}S$&^%auy3o=tv6w-?Fa}`Q5QWf0u6^cs|^HLIvQWSDBlZq0H
zDiu5e9G$%tGII-ZQgc)DN)k&l^Ygg4GII;^i%JxfQj3c6i;9)FxXKcX6jF<d!V-%z
z^Gf{7Qj5}Z^2-%$L1Otu#d?0J<r+$5U`d6{ypq)P)FOp^s468*uwxPn3R3e@!V-%-
z^GXyToMMGc9fibVh19&{{FKxbh0Ht!kQs?dIjL|{((;RP6-tXU^U@XaKz>TfEKw*Z
zN=?hG&`~JKS4b%?QAo^7Q7B3+DJ{w?275ELq9ipBWL{EfT3Tw69+$p8NM~?rDu~J`
zDJihh*9TEXdg+-Z8Kp^jnfVaDZgD|svVL-YZb4CMadBpTo?b>tZjLh4M|wr6AaQso
zrIqF-Lwy4AmIlZ*(XmODC8;_Jc?u9ZQ=v37uf)tmQvo8QP|d}etzcyf71q!&RIss8
z$kSBNRWQ`#;>=7_$W*XV$Ob9ng!l?-2gr0Cu%Q~6nwngkwOpJa`)o0#vq93C3c9um
z*<74y`9%uW3YiLawhD$u7GRd0t%5n&d>r}>Dl80YG&0o;D$LU~L1xu*;dM$aID(5(
z6JgN{ViqeT<|&ls6=$aBrKTX_H77MMy(9x3z4>Wrpg03XOj2rkW?mjBqVv-f3iK56
z6fz<4S&&$in44OXS_DcfDXGQDMVU$9)B@54iboQovnVZDuQ)Xsl+yJ~^$cO@BTpf>
z6qK5B$`dP#6_QdFl2S{`Q&aO448bYP0;Vn`BNdw5QbFl1H!(9$0g}={4o=N1$pE=j
zp*S}&CnvQ?p|~_HEwe%a<R=}4d~hyOP_$LZ%gg~8r~onzoXfzPi;DbGQ&W8Ni&7Ob
z(-aC6Qu0%a^VCZelJoOQK=vdmfD&g;Y6(1p=y8FQAt=P5i4d0X3ZMy2Q$Yik26YsQ
zQo-(pBt;!i_66bmB27?w$V^knQ?OAmRH&&@$WyRWumA~i79{3nCTl1qC8j9kDM8X*
zW|~4yYMw@crh=`lf&o}n5jX=X80aVzfQ^Ou1DsAX72wIeAle`nlFUI?L2SrWP_tEl
z<O4`v(a6&T=Ld*pNRH4jw6Q_R>KY>3nyI5u5N&7`s{;yFkR~q9{G!bC08ln7;NmP$
zu!YETae~URTxiyV<PE4t?4bTg0_%x3h~?r$*2kHtpbhs!lA58Bxh5#v=5ld@ax6%|
z0udIl%mQ*2)JdS+%9#r?!yKX|7nC8*j1{UC^z{`&{9XL56bg#+3y{i9h2)IX<ZL~K
w+{A29rYOnCELJEj(BtCdgqd#uN>G{U0pI{fEt<JFK{g`tEs9cj+|+Ub0A>j7Z~y=R

literal 0
HcmV?d00001

diff --git a/vendor/modules.txt b/vendor/modules.txt
index cf5e87e03dec0cb446390ef3e2043fa1f337cbe7..170d03040e5282e5a3f9ca4ddfd1e9471e212b05 100644
GIT binary patch
delta 61
zcmZ2yw8ePCDOT2ul9B@B$@OeHn=iArF>x0pCTA;@8R!}58BQ)>6=5`)T+boH4ie>3
PR+!wtp}IMl<Fo((i&ztQ

delta 21
dcmdmDyv}IDDb~sV**Q1=W^H5Ie4pd0003?d38Mf2

-- 
GitLab