Merge pull request #3782 from quic-go/integration-tests-versions

set the version for integration tests using a command line flag
This commit is contained in:
Marten Seemann 2023-05-01 13:59:10 +02:00 committed by GitHub
commit ea721c9c75
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 2874 additions and 2836 deletions

View file

@ -27,12 +27,18 @@ jobs:
- run:
name: "Run self integration tests"
command: go run github.com/onsi/ginkgo/v2/ginkgo -v -randomize-all -trace integrationtests/self
- run:
name: "Run version negotiation tests"
command: go run github.com/onsi/ginkgo/v2/ginkgo -v -randomize-all -trace integrationtests/versionnegotiation
- run:
name: "Run self integration tests with race detector"
command: go run github.com/onsi/ginkgo/v2/ginkgo -race -v -randomize-all -trace integrationtests/self
- run:
name: "Run self integration tests with qlog"
command: go run github.com/onsi/ginkgo/v2/ginkgo -v -randomize-all -trace integrationtests/self -- -qlog
- run:
name: "Run version negotiation tests with qlog"
command: go run github.com/onsi/ginkgo/v2/ginkgo -v -randomize-all -trace integrationtests/versionnegotiation -- -qlog
go119:
<<: *test
go120:

View file

@ -20,17 +20,24 @@ jobs:
- run: go version
- name: set qlogger
if: env.DEBUG == 'true'
run: echo "QLOGFLAG=-- -qlog" >> $GITHUB_ENV
- name: Run tests
run: echo "QLOGFLAG= -qlog" >> $GITHUB_ENV
- name: Run other tests
run: |
go run github.com/onsi/ginkgo/v2/ginkgo -r -v -randomize-all -randomize-suites -trace -skip-package self integrationtests
go run github.com/onsi/ginkgo/v2/ginkgo -r -v -randomize-all -randomize-suites -trace integrationtests/self ${{ env.QLOGFLAG }}
go run github.com/onsi/ginkgo/v2/ginkgo -r -v -randomize-all -randomize-suites -trace -skip-package self,versionnegotiation integrationtests
go run github.com/onsi/ginkgo/v2/ginkgo -r -v -randomize-all -randomize-suites -trace integrationtests/versionnegotiation -- ${{ env.QLOGFLAG }}
- name: Run self tests, using draft-29
run: go run github.com/onsi/ginkgo/v2/ginkgo -r -v -randomize-all -randomize-suites -trace integrationtests/self -- -version=draft29 ${{ env.QLOGFLAG }}
- name: Run self tests, using QUIC v1
run: go run github.com/onsi/ginkgo/v2/ginkgo -r -v -randomize-all -randomize-suites -trace integrationtests/self -- -version=1 ${{ env.QLOGFLAG }}
- name: Run self tests, using QUIC v2
run: go run github.com/onsi/ginkgo/v2/ginkgo -r -v -randomize-all -randomize-suites -trace integrationtests/self -- -version=2 ${{ env.QLOGFLAG }}
- name: Run tests (32 bit)
env:
GOARCH: 386
run: |
go run github.com/onsi/ginkgo/v2/ginkgo -r -v -randomize-all -randomize-suites -trace -skip-package self integrationtests
go run github.com/onsi/ginkgo/v2/ginkgo -r -v -randomize-all -randomize-suites -trace integrationtests/self ${{ env.QLOGFLAG }}
go run github.com/onsi/ginkgo/v2/ginkgo -r -v -randomize-all -randomize-suites -trace -skip-package self,versionnegotiation integrationtests
go run github.com/onsi/ginkgo/v2/ginkgo -r -v -randomize-all -randomize-suites -trace integrationtests/versionnegotiation -- ${{ env.QLOGFLAG }}
go run github.com/onsi/ginkgo/v2/ginkgo -r -v -randomize-all -randomize-suites -trace integrationtests/self -- ${{ env.QLOGFLAG }}
- name: save qlogs
if: ${{ always() && env.DEBUG == 'true' }}
uses: actions/upload-artifact@v2

View file

@ -33,7 +33,6 @@ const (
var defaultQuicConfig = &quic.Config{
MaxIncomingStreams: -1, // don't allow the server to create bidirectional streams
KeepAlivePeriod: 10 * time.Second,
Versions: []protocol.VersionNumber{protocol.Version1},
}
type dialFunc func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error)
@ -74,9 +73,10 @@ var _ roundTripCloser = &client{}
func newClient(hostname string, tlsConf *tls.Config, opts *roundTripperOpts, conf *quic.Config, dialer dialFunc) (roundTripCloser, error) {
if conf == nil {
conf = defaultQuicConfig.Clone()
} else if len(conf.Versions) == 0 {
}
if len(conf.Versions) == 0 {
conf = conf.Clone()
conf.Versions = []quic.VersionNumber{defaultQuicConfig.Versions[0]}
conf.Versions = []quic.VersionNumber{protocol.SupportedVersions[0]}
}
if len(conf.Versions) != 1 {
return nil, errors.New("can only use a single QUIC version for dialing a HTTP/3 connection")

View file

@ -65,7 +65,7 @@ var _ = Describe("Client", func() {
Expect(err).ToNot(HaveOccurred())
var dialAddrCalled bool
dialAddr = func(_ context.Context, _ string, tlsConf *tls.Config, quicConf *quic.Config) (quic.EarlyConnection, error) {
Expect(quicConf).To(Equal(defaultQuicConfig))
Expect(quicConf.MaxIncomingStreams).To(Equal(defaultQuicConfig.MaxIncomingStreams))
Expect(tlsConf.NextProtos).To(Equal([]string{NextProtoH3}))
Expect(quicConf.Versions).To(Equal([]protocol.VersionNumber{protocol.Version1}))
dialAddrCalled = true

View file

@ -9,17 +9,12 @@ import (
"github.com/quic-go/quic-go"
quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy"
"github.com/quic-go/quic-go/internal/protocol"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("Connection ID lengths tests", func() {
for _, v := range protocol.SupportedVersions {
version := v
Context(fmt.Sprintf("with QUIC version %s", version), func() {
It("retransmits the CONNECTION_CLOSE packet", func() {
server, err := quic.ListenAddr(
"localhost:0",
@ -51,7 +46,7 @@ var _ = Describe("Connection ID lengths tests", func() {
conn, err := quic.DialAddr(
fmt.Sprintf("localhost:%d", proxy.LocalPort()),
getTLSClientConfig(),
getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
@ -77,6 +72,4 @@ var _ = Describe("Connection ID lengths tests", func() {
Expect(<-dropped).To(Equal(first)) // these packets are all identical
}
})
})
}
})

View file

@ -32,9 +32,7 @@ func (c *connIDGenerator) ConnectionIDLen() int {
}
var _ = Describe("Connection ID lengths tests", func() {
randomConnIDLen := func() int {
return 4 + int(mrand.Int31n(15))
}
randomConnIDLen := func() int { return 4 + int(mrand.Int31n(15)) }
runServer := func(conf *quic.Config) quic.Listener {
GinkgoWriter.Write([]byte(fmt.Sprintf("Using %d byte connection ID for the server\n", conf.ConnectionIDLength)))
@ -77,46 +75,32 @@ var _ = Describe("Connection ID lengths tests", func() {
}
It("downloads a file using a 0-byte connection ID for the client", func() {
serverConf := getQuicConfig(&quic.Config{
ConnectionIDLength: randomConnIDLen(),
Versions: []protocol.VersionNumber{protocol.Version1},
})
clientConf := getQuicConfig(&quic.Config{
Versions: []protocol.VersionNumber{protocol.Version1},
})
serverConf := getQuicConfig(&quic.Config{ConnectionIDLength: randomConnIDLen()})
ln := runServer(serverConf)
defer ln.Close()
runClient(ln.Addr(), clientConf)
runClient(ln.Addr(), getQuicConfig(nil))
})
It("downloads a file when both client and server use a random connection ID length", func() {
serverConf := getQuicConfig(&quic.Config{
ConnectionIDLength: randomConnIDLen(),
Versions: []protocol.VersionNumber{protocol.Version1},
})
clientConf := getQuicConfig(&quic.Config{
ConnectionIDLength: randomConnIDLen(),
Versions: []protocol.VersionNumber{protocol.Version1},
})
serverConf := getQuicConfig(&quic.Config{ConnectionIDLength: randomConnIDLen()})
ln := runServer(serverConf)
defer ln.Close()
runClient(ln.Addr(), clientConf)
runClient(ln.Addr(), getQuicConfig(nil))
})
It("downloads a file when both client and server use a custom connection ID generator", func() {
serverConf := getQuicConfig(&quic.Config{
Versions: []protocol.VersionNumber{protocol.Version1},
ConnectionIDGenerator: &connIDGenerator{length: randomConnIDLen()},
})
clientConf := getQuicConfig(&quic.Config{
Versions: []protocol.VersionNumber{protocol.Version1},
ConnectionIDGenerator: &connIDGenerator{length: randomConnIDLen()},
})
ln := runServer(serverConf)
defer ln.Close()
runClient(ln.Addr(), clientConf)
})
})

View file

@ -12,7 +12,6 @@ import (
"github.com/quic-go/quic-go"
quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy"
"github.com/quic-go/quic-go/internal/protocol"
"github.com/quic-go/quic-go/internal/wire"
. "github.com/onsi/ginkgo/v2"
@ -20,10 +19,6 @@ import (
)
var _ = Describe("Datagram test", func() {
for _, v := range protocol.SupportedVersions {
version := v
Context(fmt.Sprintf("with QUIC version %s", version), func() {
const num = 100
var (
@ -40,10 +35,7 @@ var _ = Describe("Datagram test", func() {
ln, err := quic.Listen(
serverConn,
getTLSConfig(),
getQuicConfig(&quic.Config{
EnableDatagrams: enableDatagram,
Versions: []protocol.VersionNumber{version},
}),
getQuicConfig(&quic.Config{EnableDatagrams: enableDatagram}),
)
Expect(err).ToNot(HaveOccurred())
@ -117,10 +109,7 @@ var _ = Describe("Datagram test", func() {
raddr,
fmt.Sprintf("localhost:%d", proxy.LocalPort()),
getTLSClientConfig(),
getQuicConfig(&quic.Config{
EnableDatagrams: true,
Versions: []protocol.VersionNumber{version},
}),
getQuicConfig(&quic.Config{EnableDatagrams: true}),
)
Expect(err).ToNot(HaveOccurred())
Expect(conn.ConnectionState().SupportsDatagrams).To(BeTrue())
@ -156,10 +145,7 @@ var _ = Describe("Datagram test", func() {
raddr,
fmt.Sprintf("localhost:%d", proxy.LocalPort()),
getTLSClientConfig(),
getQuicConfig(&quic.Config{
EnableDatagrams: true,
Versions: []protocol.VersionNumber{version},
}),
getQuicConfig(&quic.Config{EnableDatagrams: true}),
)
Expect(err).ToNot(HaveOccurred())
Expect(conn.ConnectionState().SupportsDatagrams).To(BeFalse())
@ -177,10 +163,7 @@ var _ = Describe("Datagram test", func() {
raddr,
fmt.Sprintf("localhost:%d", proxy.LocalPort()),
getTLSClientConfig(),
getQuicConfig(&quic.Config{
EnableDatagrams: true,
Versions: []protocol.VersionNumber{version},
}),
getQuicConfig(&quic.Config{EnableDatagrams: true}),
)
Expect(err).ToNot(HaveOccurred())
Expect(conn.ConnectionState().SupportsDatagrams).To(BeFalse())
@ -189,6 +172,4 @@ var _ = Describe("Datagram test", func() {
conn.CloseWithError(0, "")
<-time.After(10 * time.Millisecond)
})
})
}
})

View file

@ -10,7 +10,6 @@ import (
"github.com/quic-go/quic-go"
quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy"
"github.com/quic-go/quic-go/internal/protocol"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
@ -26,12 +25,12 @@ var _ = Describe("Drop Tests", func() {
ln quic.Listener
)
startListenerAndProxy := func(dropCallback quicproxy.DropCallback, version protocol.VersionNumber) {
startListenerAndProxy := func(dropCallback quicproxy.DropCallback) {
var err error
ln, err = quic.ListenAddr(
"localhost:0",
getTLSConfig(),
getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
serverPort := ln.Addr().(*net.UDPAddr).Port
@ -51,10 +50,6 @@ var _ = Describe("Drop Tests", func() {
Expect(ln.Close()).To(Succeed())
})
for _, v := range protocol.SupportedVersions {
version := v
Context(fmt.Sprintf("with QUIC version %s", version), func() {
for _, d := range directions {
direction := d
@ -82,7 +77,7 @@ var _ = Describe("Drop Tests", func() {
atomic.AddInt32(&numDroppedPackets, 1)
}
return drop
}, version)
})
done := make(chan struct{})
go func() {
@ -104,7 +99,7 @@ var _ = Describe("Drop Tests", func() {
conn, err := quic.DialAddr(
fmt.Sprintf("localhost:%d", proxy.LocalPort()),
getTLSClientConfig(),
getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
defer conn.CloseWithError(0, "")
@ -124,6 +119,4 @@ var _ = Describe("Drop Tests", func() {
})
}
}
})
}
})

View file

@ -9,7 +9,6 @@ import (
"github.com/quic-go/quic-go"
quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy"
"github.com/quic-go/quic-go/internal/protocol"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
@ -17,15 +16,12 @@ import (
var _ = Describe("early data", func() {
const rtt = 80 * time.Millisecond
for _, v := range protocol.SupportedVersions {
version := v
Context(fmt.Sprintf("with QUIC version %s", version), func() {
It("sends 0.5-RTT data", func() {
ln, err := quic.ListenAddrEarly(
"localhost:0",
getTLSConfig(),
getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
done := make(chan struct{})
@ -56,7 +52,7 @@ var _ = Describe("early data", func() {
conn, err := quic.DialAddr(
fmt.Sprintf("localhost:%d", proxy.LocalPort()),
getTLSClientConfig(),
getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
str, err := conn.AcceptUniStream(context.Background())
@ -67,6 +63,4 @@ var _ = Describe("early data", func() {
conn.CloseWithError(0, "")
Eventually(done).Should(BeClosed())
})
})
}
})

View file

@ -15,7 +15,6 @@ import (
"github.com/quic-go/quic-go"
quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy"
"github.com/quic-go/quic-go/internal/protocol"
"github.com/quic-go/quic-go/internal/wire"
. "github.com/onsi/ginkgo/v2"
@ -27,7 +26,7 @@ var directions = []quicproxy.Direction{quicproxy.DirectionIncoming, quicproxy.Di
type applicationProtocol struct {
name string
run func(protocol.VersionNumber)
run func()
}
var _ = Describe("Handshake drop tests", func() {
@ -39,11 +38,10 @@ var _ = Describe("Handshake drop tests", func() {
data := GeneratePRData(5000)
const timeout = 2 * time.Minute
startListenerAndProxy := func(dropCallback quicproxy.DropCallback, doRetry bool, longCertChain bool, version protocol.VersionNumber) {
startListenerAndProxy := func(dropCallback quicproxy.DropCallback, doRetry bool, longCertChain bool) {
conf := getQuicConfig(&quic.Config{
MaxIdleTimeout: timeout,
HandshakeIdleTimeout: timeout,
Versions: []protocol.VersionNumber{version},
RequireAddressValidation: func(net.Addr) bool { return doRetry },
})
var tlsConf *tls.Config
@ -68,7 +66,7 @@ var _ = Describe("Handshake drop tests", func() {
clientSpeaksFirst := &applicationProtocol{
name: "client speaks first",
run: func(version protocol.VersionNumber) {
run: func() {
serverConnChan := make(chan quic.Connection)
go func() {
defer GinkgoRecover()
@ -88,7 +86,6 @@ var _ = Describe("Handshake drop tests", func() {
getQuicConfig(&quic.Config{
MaxIdleTimeout: timeout,
HandshakeIdleTimeout: timeout,
Versions: []protocol.VersionNumber{version},
}),
)
Expect(err).ToNot(HaveOccurred())
@ -107,7 +104,7 @@ var _ = Describe("Handshake drop tests", func() {
serverSpeaksFirst := &applicationProtocol{
name: "server speaks first",
run: func(version protocol.VersionNumber) {
run: func() {
serverConnChan := make(chan quic.Connection)
go func() {
defer GinkgoRecover()
@ -126,7 +123,6 @@ var _ = Describe("Handshake drop tests", func() {
getQuicConfig(&quic.Config{
MaxIdleTimeout: timeout,
HandshakeIdleTimeout: timeout,
Versions: []protocol.VersionNumber{version},
}),
)
Expect(err).ToNot(HaveOccurred())
@ -145,7 +141,7 @@ var _ = Describe("Handshake drop tests", func() {
nobodySpeaks := &applicationProtocol{
name: "nobody speaks",
run: func(version protocol.VersionNumber) {
run: func() {
serverConnChan := make(chan quic.Connection)
go func() {
defer GinkgoRecover()
@ -159,7 +155,6 @@ var _ = Describe("Handshake drop tests", func() {
getQuicConfig(&quic.Config{
MaxIdleTimeout: timeout,
HandshakeIdleTimeout: timeout,
Versions: []protocol.VersionNumber{version},
}),
)
Expect(err).ToNot(HaveOccurred())
@ -176,10 +171,6 @@ var _ = Describe("Handshake drop tests", func() {
Expect(proxy.Close()).To(Succeed())
})
for _, v := range protocol.SupportedVersions {
version := v
Context(fmt.Sprintf("with QUIC version %s", version), func() {
for _, d := range directions {
direction := d
@ -211,8 +202,8 @@ var _ = Describe("Handshake drop tests", func() {
p = atomic.AddInt32(&outgoing, 1)
}
return p == 1 && d.Is(direction)
}, doRetry, longCertChain, version)
app.run(version)
}, doRetry, longCertChain)
app.run()
})
It(fmt.Sprintf("establishes a connection when the second packet is lost in %s direction", direction), func() {
@ -227,8 +218,8 @@ var _ = Describe("Handshake drop tests", func() {
p = atomic.AddInt32(&outgoing, 1)
}
return p == 2 && d.Is(direction)
}, doRetry, longCertChain, version)
app.run(version)
}, doRetry, longCertChain)
app.run()
})
It(fmt.Sprintf("establishes a connection when 1/3 of the packets are lost in %s direction", direction), func() {
@ -265,8 +256,8 @@ var _ = Describe("Handshake drop tests", func() {
}
}
return drop
}, doRetry, longCertChain, version)
app.run(version)
}, doRetry, longCertChain)
app.run()
})
})
}
@ -274,8 +265,6 @@ var _ = Describe("Handshake drop tests", func() {
}
})
}
}
})
It("establishes a connection when the ClientHello is larger than 1 MTU (e.g. post-quantum)", func() {
origAdditionalTransportParametersClient := wire.AdditionalTransportParametersClient
@ -294,8 +283,8 @@ var _ = Describe("Handshake drop tests", func() {
return false
}
return mrand.Intn(3) == 0
}, false, false, version)
clientSpeaksFirst.run(version)
}, false, false)
clientSpeaksFirst.run()
})
}
})

View file

@ -10,7 +10,6 @@ import (
"github.com/quic-go/quic-go"
quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy"
"github.com/quic-go/quic-go/internal/protocol"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
@ -53,35 +52,6 @@ var _ = Describe("Handshake RTT tests", func() {
))
}
It("fails when there's no matching version, after 1 RTT", func() {
if len(protocol.SupportedVersions) == 1 {
Skip("Test requires at least 2 supported versions.")
}
serverConfig.Versions = protocol.SupportedVersions[:1]
ln, err := quic.ListenAddr("localhost:0", serverTLSConfig, serverConfig)
Expect(err).ToNot(HaveOccurred())
defer ln.Close()
runProxy(ln.Addr())
startTime := time.Now()
_, err = quic.DialAddr(
proxy.LocalAddr().String(),
getTLSClientConfig(),
getQuicConfig(&quic.Config{Versions: protocol.SupportedVersions[1:2]}),
)
Expect(err).To(HaveOccurred())
expectDurationInRTTs(startTime, 1)
})
var clientConfig *quic.Config
BeforeEach(func() {
serverConfig.Versions = []protocol.VersionNumber{protocol.Version1}
clientConfig = getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{protocol.Version1}})
clientConfig := getTLSClientConfig()
clientConfig.InsecureSkipVerify = true
})
// 1 RTT for verifying the source address
// 1 RTT for the TLS handshake
It("is forward-secure after 2 RTTs", func() {
@ -95,7 +65,7 @@ var _ = Describe("Handshake RTT tests", func() {
_, err = quic.DialAddr(
fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port),
getTLSClientConfig(),
clientConfig,
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
expectDurationInRTTs(startTime, 2)
@ -111,7 +81,7 @@ var _ = Describe("Handshake RTT tests", func() {
_, err = quic.DialAddr(
fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port),
getTLSClientConfig(),
clientConfig,
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
expectDurationInRTTs(startTime, 1)
@ -128,7 +98,7 @@ var _ = Describe("Handshake RTT tests", func() {
_, err = quic.DialAddr(
fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port),
getTLSClientConfig(),
clientConfig,
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
expectDurationInRTTs(startTime, 2)
@ -138,6 +108,7 @@ var _ = Describe("Handshake RTT tests", func() {
ln, err := quic.ListenAddr("localhost:0", serverTLSConfig, serverConfig)
Expect(err).ToNot(HaveOccurred())
go func() {
defer GinkgoRecover()
conn, err := ln.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())
str, err := conn.OpenUniStream()
@ -153,7 +124,7 @@ var _ = Describe("Handshake RTT tests", func() {
conn, err := quic.DialAddr(
fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port),
getTLSClientConfig(),
clientConfig,
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
str, err := conn.AcceptUniStream(context.Background())
@ -168,6 +139,7 @@ var _ = Describe("Handshake RTT tests", func() {
ln, err := quic.ListenAddrEarly("localhost:0", serverTLSConfig, serverConfig)
Expect(err).ToNot(HaveOccurred())
go func() {
defer GinkgoRecover()
conn, err := ln.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())
// Check the ALPN now. This is probably what an application would do.
@ -186,7 +158,7 @@ var _ = Describe("Handshake RTT tests", func() {
conn, err := quic.DialAddr(
fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port),
getTLSClientConfig(),
clientConfig,
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
str, err := conn.AcceptUniStream(context.Background())

View file

@ -10,20 +10,14 @@ import (
"time"
"github.com/quic-go/quic-go"
"github.com/quic-go/quic-go/integrationtests/tools/israce"
"github.com/quic-go/quic-go/internal/protocol"
"github.com/quic-go/quic-go/internal/qerr"
"github.com/quic-go/quic-go/internal/qtls"
"github.com/quic-go/quic-go/logging"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
type versioner interface {
GetVersion() protocol.VersionNumber
}
type tokenStore struct {
store quic.TokenStore
gets chan<- string
@ -50,31 +44,6 @@ func (c *tokenStore) Pop(key string) *quic.ClientToken {
return c.store.Pop(key)
}
type versionNegotiationTracer struct {
logging.NullConnectionTracer
loggedVersions bool
receivedVersionNegotiation bool
chosen logging.VersionNumber
clientVersions, serverVersions []logging.VersionNumber
}
var _ logging.ConnectionTracer = &versionNegotiationTracer{}
func (t *versionNegotiationTracer) NegotiatedVersion(chosen logging.VersionNumber, clientVersions, serverVersions []logging.VersionNumber) {
if t.loggedVersions {
Fail("only expected one call to NegotiatedVersions")
}
t.loggedVersions = true
t.chosen = chosen
t.clientVersions = clientVersions
t.serverVersions = serverVersions
}
func (t *versionNegotiationTracer) ReceivedVersionNegotiationPacket(dest, src logging.ArbitraryLenConnectionID, _ []logging.VersionNumber) {
t.receivedVersionNegotiation = true
}
var _ = Describe("Handshake tests", func() {
var (
server quic.Listener
@ -112,79 +81,6 @@ var _ = Describe("Handshake tests", func() {
}()
}
if !israce.Enabled {
Context("Version Negotiation", func() {
var supportedVersions []protocol.VersionNumber
BeforeEach(func() {
supportedVersions = protocol.SupportedVersions
protocol.SupportedVersions = append(protocol.SupportedVersions, []protocol.VersionNumber{7, 8, 9, 10}...)
})
AfterEach(func() {
protocol.SupportedVersions = supportedVersions
})
It("when the server supports more versions than the client", func() {
expectedVersion := protocol.SupportedVersions[0]
// the server doesn't support the highest supported version, which is the first one the client will try
// but it supports a bunch of versions that the client doesn't speak
serverConfig.Versions = []protocol.VersionNumber{7, 8, protocol.SupportedVersions[0], 9}
serverTracer := &versionNegotiationTracer{}
serverConfig.Tracer = newTracer(func() logging.ConnectionTracer { return serverTracer })
runServer(getTLSConfig())
defer server.Close()
clientTracer := &versionNegotiationTracer{}
conn, err := quic.DialAddr(
fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
getTLSClientConfig(),
getQuicConfig(&quic.Config{Tracer: newTracer(func() logging.ConnectionTracer { return clientTracer })}),
)
Expect(err).ToNot(HaveOccurred())
Expect(conn.(versioner).GetVersion()).To(Equal(expectedVersion))
Expect(conn.CloseWithError(0, "")).To(Succeed())
Expect(clientTracer.chosen).To(Equal(expectedVersion))
Expect(clientTracer.receivedVersionNegotiation).To(BeFalse())
Expect(clientTracer.clientVersions).To(Equal(protocol.SupportedVersions))
Expect(clientTracer.serverVersions).To(BeEmpty())
Expect(serverTracer.chosen).To(Equal(expectedVersion))
Expect(serverTracer.serverVersions).To(Equal(serverConfig.Versions))
Expect(serverTracer.clientVersions).To(BeEmpty())
})
It("when the client supports more versions than the server supports", func() {
expectedVersion := protocol.SupportedVersions[0]
// the server doesn't support the highest supported version, which is the first one the client will try
// but it supports a bunch of versions that the client doesn't speak
serverConfig.Versions = supportedVersions
serverTracer := &versionNegotiationTracer{}
serverConfig.Tracer = newTracer(func() logging.ConnectionTracer { return serverTracer })
runServer(getTLSConfig())
defer server.Close()
clientVersions := []protocol.VersionNumber{7, 8, 9, protocol.SupportedVersions[0], 10}
clientTracer := &versionNegotiationTracer{}
conn, err := quic.DialAddr(
fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
getTLSClientConfig(),
getQuicConfig(&quic.Config{
Versions: clientVersions,
Tracer: newTracer(func() logging.ConnectionTracer { return clientTracer }),
}),
)
Expect(err).ToNot(HaveOccurred())
Expect(conn.(versioner).GetVersion()).To(Equal(protocol.SupportedVersions[0]))
Expect(conn.CloseWithError(0, "")).To(Succeed())
Expect(clientTracer.chosen).To(Equal(expectedVersion))
Expect(clientTracer.receivedVersionNegotiation).To(BeTrue())
Expect(clientTracer.clientVersions).To(Equal(clientVersions))
Expect(clientTracer.serverVersions).To(ContainElements(supportedVersions)) // may contain greased versions
Expect(serverTracer.chosen).To(Equal(expectedVersion))
Expect(serverTracer.serverVersions).To(Equal(serverConfig.Versions))
Expect(serverTracer.clientVersions).To(BeEmpty())
})
})
}
Context("using different cipher suites", func() {
for n, id := range map[string]uint16{
"TLS_AES_128_GCM_SHA256": tls.TLS_AES_128_GCM_SHA256,
@ -232,23 +128,12 @@ var _ = Describe("Handshake tests", func() {
})
Context("Certificate validation", func() {
for _, v := range protocol.SupportedVersions {
version := v
Context(fmt.Sprintf("using %s", version), func() {
var clientConfig *quic.Config
BeforeEach(func() {
serverConfig.Versions = []protocol.VersionNumber{version}
clientConfig = getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}})
})
It("accepts the certificate", func() {
runServer(getTLSConfig())
_, err := quic.DialAddr(
fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
getTLSClientConfig(),
clientConfig,
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
})
@ -258,7 +143,7 @@ var _ = Describe("Handshake tests", func() {
_, err := quic.DialAddr(
fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
getTLSClientConfig(),
getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
})
@ -272,7 +157,7 @@ var _ = Describe("Handshake tests", func() {
server.Addr(),
"foo.bar",
getTLSClientConfig(),
clientConfig,
getQuicConfig(nil),
)
Expect(err).To(HaveOccurred())
var transportErr *quic.TransportError
@ -289,7 +174,7 @@ var _ = Describe("Handshake tests", func() {
conn, err := quic.DialAddr(
fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
getTLSClientConfig(),
clientConfig,
getQuicConfig(nil),
)
// Usually, the error will occur after the client already finished the handshake.
// However, there's a race condition here. The server's CONNECTION_CLOSE might be
@ -317,7 +202,7 @@ var _ = Describe("Handshake tests", func() {
_, err := quic.DialAddr(
fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
tlsConf,
clientConfig,
getQuicConfig(nil),
)
Expect(err).To(HaveOccurred())
var transportErr *quic.TransportError
@ -326,8 +211,6 @@ var _ = Describe("Handshake tests", func() {
Expect(transportErr.Error()).To(ContainSubstring("x509: certificate is valid for localhost, not foo.bar"))
})
})
}
})
Context("rate limiting", func() {
var (

View file

@ -3,7 +3,6 @@ package self_test
import (
"context"
"crypto/tls"
"fmt"
"io"
"net"
"net/http"
@ -13,7 +12,6 @@ import (
"github.com/quic-go/quic-go"
"github.com/quic-go/quic-go/http3"
"github.com/quic-go/quic-go/internal/protocol"
"github.com/quic-go/quic-go/internal/testdata"
. "github.com/onsi/ginkgo/v2"
@ -71,8 +69,6 @@ var _ = Describe("HTTP3 Server hotswap test", func() {
port string
)
versions := protocol.SupportedVersions
BeforeEach(func() {
mux1 = http.NewServeMux()
mux1.HandleFunc("/hello1", func(w http.ResponseWriter, r *http.Request) {
@ -89,17 +85,17 @@ var _ = Describe("HTTP3 Server hotswap test", func() {
server1 = &http3.Server{
Handler: mux1,
TLSConfig: testdata.GetTLSConfig(),
QuicConfig: getQuicConfig(&quic.Config{Versions: versions}),
QuicConfig: getQuicConfig(nil),
}
server2 = &http3.Server{
Handler: mux2,
TLSConfig: testdata.GetTLSConfig(),
QuicConfig: getQuicConfig(&quic.Config{Versions: versions}),
QuicConfig: getQuicConfig(nil),
}
tlsConf := http3.ConfigureTLSConfig(testdata.GetTLSConfig())
quicln, err := quic.ListenAddrEarly("0.0.0.0:0", tlsConf, getQuicConfig(&quic.Config{Versions: versions}))
quicln, err := quic.ListenAddrEarly("0.0.0.0:0", tlsConf, getQuicConfig(nil))
ln = &listenerWrapper{EarlyListener: quicln}
Expect(err).NotTo(HaveOccurred())
port = strconv.Itoa(ln.Addr().(*net.UDPAddr).Port)
@ -109,10 +105,6 @@ var _ = Describe("HTTP3 Server hotswap test", func() {
Expect(ln.Close()).NotTo(HaveOccurred())
})
for _, v := range versions {
version := v
Context(fmt.Sprintf("with QUIC version %s", version), func() {
BeforeEach(func() {
client = &http.Client{
Transport: &http3.RoundTripper{
@ -120,10 +112,7 @@ var _ = Describe("HTTP3 Server hotswap test", func() {
RootCAs: testdata.GetRootCA(),
},
DisableCompression: true,
QuicConfig: getQuicConfig(&quic.Config{
Versions: []protocol.VersionNumber{version},
MaxIdleTimeout: 10 * time.Second,
}),
QuicConfig: getQuicConfig(&quic.Config{MaxIdleTimeout: 10 * time.Second}),
},
}
})
@ -181,6 +170,4 @@ var _ = Describe("HTTP3 Server hotswap test", func() {
Expect(fake2.closed).To(Equal(int32(1)))
Expect(ln.listenerClosed).To(BeTrue())
})
})
}
})

View file

@ -46,8 +46,6 @@ var _ = Describe("HTTP tests", func() {
port string
)
versions := protocol.SupportedVersions
BeforeEach(func() {
mux = http.NewServeMux()
mux.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
@ -83,7 +81,7 @@ var _ = Describe("HTTP tests", func() {
server = &http3.Server{
Handler: mux,
TLSConfig: testdata.GetTLSConfig(),
QuicConfig: getQuicConfig(&quic.Config{Versions: versions}),
QuicConfig: getQuicConfig(nil),
}
addr, err := net.ResolveUDPAddr("udp", "0.0.0.0:0")
@ -106,10 +104,6 @@ var _ = Describe("HTTP tests", func() {
Eventually(stoppedServing).Should(BeClosed())
})
for _, v := range versions {
version := v
Context(fmt.Sprintf("with QUIC version %s", version), func() {
BeforeEach(func() {
client = &http.Client{
Transport: &http3.RoundTripper{
@ -117,10 +111,7 @@ var _ = Describe("HTTP tests", func() {
RootCAs: testdata.GetRootCA(),
},
DisableCompression: true,
QuicConfig: getQuicConfig(&quic.Config{
Versions: []protocol.VersionNumber{version},
MaxIdleTimeout: 10 * time.Second,
}),
QuicConfig: getQuicConfig(&quic.Config{MaxIdleTimeout: 10 * time.Second}),
},
}
})
@ -386,6 +377,30 @@ var _ = Describe("HTTP tests", func() {
Expect(repl).To(Equal(data))
})
It("serves other QUIC connections", func() {
if version == protocol.VersionDraft29 {
Skip("This test only works on RFC versions")
}
tlsConf := testdata.GetTLSConfig()
tlsConf.NextProtos = []string{"h3"}
ln, err := quic.ListenAddr("localhost:0", tlsConf, nil)
Expect(err).ToNot(HaveOccurred())
done := make(chan struct{})
go func() {
defer GinkgoRecover()
defer close(done)
conn, err := ln.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())
Expect(server.ServeQUICConn(conn)).To(Succeed())
}()
resp, err := client.Get(fmt.Sprintf("https://localhost:%d/hello", ln.Addr().(*net.UDPAddr).Port))
Expect(err).ToNot(HaveOccurred())
Expect(resp.StatusCode).To(Equal(http.StatusOK))
client.Transport.(io.Closer).Close()
Eventually(done).Should(BeClosed())
})
It("supports read deadlines", func() {
if !go120 {
Skip("This test requires Go 1.20+")
@ -439,29 +454,4 @@ var _ = Describe("HTTP tests", func() {
Expect(time.Now().After(expectedEnd)).To(BeTrue())
Expect(string(body)).To(ContainSubstring("aa"))
})
if version != protocol.VersionDraft29 {
It("serves other QUIC connections", func() {
tlsConf := testdata.GetTLSConfig()
tlsConf.NextProtos = []string{"h3"}
ln, err := quic.ListenAddr("localhost:0", tlsConf, nil)
Expect(err).ToNot(HaveOccurred())
done := make(chan struct{})
go func() {
defer GinkgoRecover()
defer close(done)
conn, err := ln.Accept(context.Background())
Expect(err).ToNot(HaveOccurred())
Expect(server.ServeQUICConn(conn)).To(Succeed())
}()
resp, err := client.Get(fmt.Sprintf("https://localhost:%d/hello", ln.Addr().(*net.UDPAddr).Port))
Expect(err).ToNot(HaveOccurred())
Expect(resp.StatusCode).To(Equal(http.StatusOK))
client.Transport.(io.Closer).Close()
Eventually(done).Should(BeClosed())
})
}
})
}
})

View file

@ -22,10 +22,6 @@ import (
)
var _ = Describe("MITM test", func() {
for _, v := range protocol.SupportedVersions {
version := v
Context(fmt.Sprintf("with QUIC version %s", version), func() {
const connIDLen = 6 // explicitly set the connection ID length, so the proxy can parse it
var (
@ -72,10 +68,7 @@ var _ = Describe("MITM test", func() {
}
BeforeEach(func() {
serverConfig = getQuicConfig(&quic.Config{
Versions: []protocol.VersionNumber{version},
ConnectionIDLength: connIDLen,
})
serverConfig = getQuicConfig(&quic.Config{ConnectionIDLength: connIDLen})
addr, err := net.ResolveUDPAddr("udp", "localhost:0")
Expect(err).ToNot(HaveOccurred())
clientUDPConn, err = net.ListenUDP("udp", addr)
@ -116,7 +109,7 @@ var _ = Describe("MITM test", func() {
for i := 0; i < numPackets; i++ {
payloadLen := mrand.Int31n(100)
replyHdr.Length = protocol.ByteCount(mrand.Int31n(payloadLen + 1))
b, err := replyHdr.Append(nil, version)
b, err := replyHdr.Append(nil, hdr.Version)
Expect(err).ToNot(HaveOccurred())
r := make([]byte, payloadLen)
mrand.Read(r)
@ -158,10 +151,7 @@ var _ = Describe("MITM test", func() {
raddr,
fmt.Sprintf("localhost:%d", proxyPort),
getTLSClientConfig(),
getQuicConfig(&quic.Config{
Versions: []protocol.VersionNumber{version},
ConnectionIDLength: connIDLen,
}),
getQuicConfig(&quic.Config{ConnectionIDLength: connIDLen}),
)
Expect(err).ToNot(HaveOccurred())
str, err := conn.AcceptUniStream(context.Background())
@ -205,10 +195,7 @@ var _ = Describe("MITM test", func() {
raddr,
fmt.Sprintf("localhost:%d", proxyPort),
getTLSClientConfig(),
getQuicConfig(&quic.Config{
Versions: []protocol.VersionNumber{version},
ConnectionIDLength: connIDLen,
}),
getQuicConfig(&quic.Config{ConnectionIDLength: connIDLen}),
)
Expect(err).ToNot(HaveOccurred())
str, err := conn.AcceptUniStream(context.Background())
@ -324,7 +311,6 @@ var _ = Describe("MITM test", func() {
fmt.Sprintf("localhost:%d", proxyPort),
getTLSClientConfig(),
getQuicConfig(&quic.Config{
Versions: []protocol.VersionNumber{version},
ConnectionIDLength: connIDLen,
HandshakeIdleTimeout: 2 * time.Second,
}),
@ -467,6 +453,4 @@ var _ = Describe("MITM test", func() {
Eventually(done).Should(BeClosed())
})
})
})
}
})

View file

@ -9,17 +9,12 @@ import (
"time"
"github.com/quic-go/quic-go"
"github.com/quic-go/quic-go/internal/protocol"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("Multiplexing", func() {
for _, v := range protocol.SupportedVersions {
version := v
Context(fmt.Sprintf("with QUIC version %s", version), func() {
runServer := func(ln quic.Listener) {
go func() {
defer GinkgoRecover()
@ -46,7 +41,7 @@ var _ = Describe("Multiplexing", func() {
addr,
fmt.Sprintf("localhost:%d", addr.(*net.UDPAddr).Port),
getTLSClientConfig(),
getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
defer conn.CloseWithError(0, "")
@ -62,7 +57,7 @@ var _ = Describe("Multiplexing", func() {
ln, err := quic.ListenAddr(
"localhost:0",
getTLSConfig(),
getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
return ln
@ -145,7 +140,7 @@ var _ = Describe("Multiplexing", func() {
server, err := quic.Listen(
conn,
getTLSConfig(),
getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
runServer(server)
@ -181,7 +176,7 @@ var _ = Describe("Multiplexing", func() {
server1, err := quic.Listen(
conn1,
getTLSConfig(),
getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
runServer(server1)
@ -190,7 +185,7 @@ var _ = Describe("Multiplexing", func() {
server2, err := quic.Listen(
conn2,
getTLSConfig(),
getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
runServer(server2)
@ -216,6 +211,4 @@ var _ = Describe("Multiplexing", func() {
Eventually(done2, timeout).Should(BeClosed())
})
})
})
}
})

View file

@ -9,21 +9,17 @@ import (
"github.com/quic-go/quic-go"
quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy"
"github.com/quic-go/quic-go/internal/protocol"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("non-zero RTT", func() {
for _, v := range protocol.SupportedVersions {
version := v
runServer := func() quic.Listener {
ln, err := quic.ListenAddr(
"localhost:0",
getTLSConfig(),
getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
go func() {
@ -43,7 +39,7 @@ var _ = Describe("non-zero RTT", func() {
conn, err := quic.DialAddr(
fmt.Sprintf("localhost:%d", port),
getTLSClientConfig(),
getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
str, err := conn.AcceptStream(context.Background())
@ -54,7 +50,6 @@ var _ = Describe("non-zero RTT", func() {
conn.CloseWithError(0, "")
}
Context(fmt.Sprintf("with QUIC version %s", version), func() {
for _, r := range [...]time.Duration{
10 * time.Millisecond,
50 * time.Millisecond,
@ -79,7 +74,7 @@ var _ = Describe("non-zero RTT", func() {
conn, err := quic.DialAddr(
fmt.Sprintf("localhost:%d", proxy.LocalPort()),
getTLSClientConfig(),
getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
str, err := conn.AcceptStream(context.Background())
@ -113,6 +108,4 @@ var _ = Describe("non-zero RTT", func() {
downloadFile(proxy.LocalPort())
})
}
})
}
})

View file

@ -1,20 +1,13 @@
package self_test
import (
"bufio"
"bytes"
"context"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"flag"
"fmt"
"io"
"log"
"math/big"
mrand "math/rand"
"os"
"runtime/pprof"
@ -24,19 +17,18 @@ import (
"testing"
"time"
"golang.org/x/crypto/ed25519"
"github.com/quic-go/quic-go"
"github.com/quic-go/quic-go/integrationtests/tools"
"github.com/quic-go/quic-go/internal/protocol"
"github.com/quic-go/quic-go/internal/utils"
"github.com/quic-go/quic-go/internal/wire"
"github.com/quic-go/quic-go/logging"
"github.com/quic-go/quic-go/qlog"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
const alpn = "quic-go integration tests"
const alpn = tools.ALPN
const (
dataLen = 500 * 1024 // 500 KB
@ -93,25 +85,29 @@ var (
logFileName string // the log file set in the ginkgo flags
logBufOnce sync.Once
logBuf *syncedBuffer
versionParam string
qlogTracer logging.Tracer
enableQlog bool
version quic.VersionNumber
tlsConfig *tls.Config
tlsConfigLongChain *tls.Config
tlsClientConfig *tls.Config
quicConfigTracer logging.Tracer
)
// read the logfile command line flag
// to set call ginkgo -- -logfile=log.txt
func init() {
flag.StringVar(&logFileName, "logfile", "", "log file")
flag.StringVar(&versionParam, "version", "1", "QUIC version")
flag.BoolVar(&enableQlog, "qlog", false, "enable qlog")
ca, caPrivateKey, err := generateCA()
ca, caPrivateKey, err := tools.GenerateCA()
if err != nil {
panic(err)
}
leafCert, leafPrivateKey, err := generateLeafCert(ca, caPrivateKey)
leafCert, leafPrivateKey, err := tools.GenerateLeafCert(ca, caPrivateKey)
if err != nil {
panic(err)
}
@ -122,7 +118,7 @@ func init() {
}},
NextProtos: []string{alpn},
}
tlsConfLongChain, err := generateTLSConfigWithLongCertChain(ca, caPrivateKey)
tlsConfLongChain, err := tools.GenerateTLSConfigWithLongCertChain(ca, caPrivateKey)
if err != nil {
panic(err)
}
@ -140,126 +136,22 @@ var _ = BeforeSuite(func() {
mrand.Seed(GinkgoRandomSeed())
if enableQlog {
quicConfigTracer = qlog.NewTracer(func(p logging.Perspective, connectionID []byte) io.WriteCloser {
role := "server"
if p == logging.PerspectiveClient {
role = "client"
qlogTracer = tools.NewQlogger(GinkgoWriter)
}
filename := fmt.Sprintf("log_%x_%s.qlog", connectionID, role)
fmt.Fprintf(GinkgoWriter, "Creating %s.\n", filename)
f, err := os.Create(filename)
Expect(err).ToNot(HaveOccurred())
bw := bufio.NewWriter(f)
return utils.NewBufferedWriteCloser(bw, f)
})
switch versionParam {
case "1":
version = quic.Version1
case "2":
version = quic.Version2
case "draft29":
version = quic.VersionDraft29
default:
Fail(fmt.Sprintf("unknown QUIC version: %s", versionParam))
}
fmt.Printf("Using QUIC version: %s\n", version)
protocol.SupportedVersions = []quic.VersionNumber{version}
})
func generateCA() (*x509.Certificate, crypto.PrivateKey, error) {
certTempl := &x509.Certificate{
SerialNumber: big.NewInt(2019),
Subject: pkix.Name{},
NotBefore: time.Now(),
NotAfter: time.Now().Add(24 * time.Hour),
IsCA: true,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
BasicConstraintsValid: true,
}
pub, priv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return nil, nil, err
}
caBytes, err := x509.CreateCertificate(rand.Reader, certTempl, certTempl, pub, priv)
if err != nil {
return nil, nil, err
}
ca, err := x509.ParseCertificate(caBytes)
if err != nil {
return nil, nil, err
}
return ca, priv, nil
}
func generateLeafCert(ca *x509.Certificate, caPriv crypto.PrivateKey) (*x509.Certificate, crypto.PrivateKey, error) {
certTempl := &x509.Certificate{
SerialNumber: big.NewInt(1),
DNSNames: []string{"localhost"},
NotBefore: time.Now(),
NotAfter: time.Now().Add(24 * time.Hour),
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature,
}
pub, priv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return nil, nil, err
}
certBytes, err := x509.CreateCertificate(rand.Reader, certTempl, ca, pub, caPriv)
if err != nil {
return nil, nil, err
}
cert, err := x509.ParseCertificate(certBytes)
if err != nil {
return nil, nil, err
}
return cert, priv, nil
}
// getTLSConfigWithLongCertChain generates a tls.Config that uses a long certificate chain.
// The Root CA used is the same as for the config returned from getTLSConfig().
func generateTLSConfigWithLongCertChain(ca *x509.Certificate, caPrivateKey crypto.PrivateKey) (*tls.Config, error) {
const chainLen = 7
certTempl := &x509.Certificate{
SerialNumber: big.NewInt(2019),
Subject: pkix.Name{},
NotBefore: time.Now(),
NotAfter: time.Now().Add(24 * time.Hour),
IsCA: true,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
BasicConstraintsValid: true,
}
lastCA := ca
lastCAPrivKey := caPrivateKey
privKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
certs := make([]*x509.Certificate, chainLen)
for i := 0; i < chainLen; i++ {
caBytes, err := x509.CreateCertificate(rand.Reader, certTempl, lastCA, &privKey.PublicKey, lastCAPrivKey)
if err != nil {
return nil, err
}
ca, err := x509.ParseCertificate(caBytes)
if err != nil {
return nil, err
}
certs[i] = ca
lastCA = ca
lastCAPrivKey = privKey
}
leafCert, leafPrivateKey, err := generateLeafCert(lastCA, lastCAPrivKey)
if err != nil {
return nil, err
}
rawCerts := make([][]byte, chainLen+1)
for i, cert := range certs {
rawCerts[chainLen-i] = cert.Raw
}
rawCerts[0] = leafCert.Raw
return &tls.Config{
Certificates: []tls.Certificate{{
Certificate: rawCerts,
PrivateKey: leafPrivateKey,
}},
NextProtos: []string{alpn},
}, nil
}
func getTLSConfig() *tls.Config {
return tlsConfig.Clone()
}
@ -278,10 +170,12 @@ func getQuicConfig(conf *quic.Config) *quic.Config {
} else {
conf = conf.Clone()
}
if enableQlog {
if conf.Tracer == nil {
conf.Tracer = quicConfigTracer
} else if quicConfigTracer != nil {
conf.Tracer = logging.NewMultiplexedTracer(quicConfigTracer, conf.Tracer)
conf.Tracer = qlogTracer
} else if qlogTracer != nil {
conf.Tracer = logging.NewMultiplexedTracer(qlogTracer, conf.Tracer)
}
}
return conf
}

View file

@ -7,11 +7,9 @@ import (
"net"
"sync"
"github.com/quic-go/quic-go"
"github.com/quic-go/quic-go/internal/protocol"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/quic-go/quic-go"
)
var _ = Describe("Bidirectional streams", func() {
@ -20,20 +18,11 @@ var _ = Describe("Bidirectional streams", func() {
var (
server quic.Listener
serverAddr string
qconf *quic.Config
)
for _, v := range []protocol.VersionNumber{protocol.Version1} {
version := v
Context(fmt.Sprintf("with QUIC %s", version), func() {
BeforeEach(func() {
var err error
qconf = &quic.Config{
Versions: []protocol.VersionNumber{version},
MaxIncomingStreams: 0,
}
server, err = quic.ListenAddr("localhost:0", getTLSConfig(), getQuicConfig(qconf))
server, err = quic.ListenAddr("localhost:0", getTLSConfig(), getQuicConfig(nil))
Expect(err).ToNot(HaveOccurred())
serverAddr = fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port)
})
@ -100,7 +89,7 @@ var _ = Describe("Bidirectional streams", func() {
client, err := quic.DialAddr(
serverAddr,
getTLSClientConfig(),
getQuicConfig(qconf),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
runSendingPeer(client)
@ -118,7 +107,7 @@ var _ = Describe("Bidirectional streams", func() {
client, err := quic.DialAddr(
serverAddr,
getTLSClientConfig(),
getQuicConfig(qconf),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
runReceivingPeer(client)
@ -145,7 +134,7 @@ var _ = Describe("Bidirectional streams", func() {
client, err := quic.DialAddr(
serverAddr,
getTLSClientConfig(),
getQuicConfig(qconf),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
done2 := make(chan struct{})
@ -158,6 +147,4 @@ var _ = Describe("Bidirectional streams", func() {
<-done1
<-done2
})
})
}
})

View file

@ -20,13 +20,11 @@ var _ = Describe("Unidirectional Streams", func() {
var (
server quic.Listener
serverAddr string
qconf *quic.Config
)
BeforeEach(func() {
var err error
qconf = &quic.Config{Versions: []protocol.VersionNumber{protocol.Version1}}
server, err = quic.ListenAddr("localhost:0", getTLSConfig(), getQuicConfig(qconf))
server, err = quic.ListenAddr("localhost:0", getTLSConfig(), getQuicConfig(nil))
Expect(err).ToNot(HaveOccurred())
serverAddr = fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port)
})
@ -81,7 +79,7 @@ var _ = Describe("Unidirectional Streams", func() {
client, err := quic.DialAddr(
serverAddr,
getTLSClientConfig(),
getQuicConfig(qconf),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
runSendingPeer(client)
@ -99,7 +97,7 @@ var _ = Describe("Unidirectional Streams", func() {
client, err := quic.DialAddr(
serverAddr,
getTLSClientConfig(),
getQuicConfig(qconf),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
runReceivingPeer(client)
@ -125,7 +123,7 @@ var _ = Describe("Unidirectional Streams", func() {
client, err := quic.DialAddr(
serverAddr,
getTLSClientConfig(),
getQuicConfig(qconf),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
done2 := make(chan struct{})

View file

@ -25,10 +25,6 @@ import (
var _ = Describe("0-RTT", func() {
rtt := scaleDuration(5 * time.Millisecond)
for _, v := range protocol.SupportedVersions {
version := v
Context(fmt.Sprintf("with QUIC version %s", version), func() {
runCountingProxy := func(serverPort int) (*quicproxy.QuicProxy, *uint32) {
var num0RTTPackets uint32 // to be used as an atomic
proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
@ -58,7 +54,6 @@ var _ = Describe("0-RTT", func() {
tlsConf := getTLSConfig()
if serverConf == nil {
serverConf = getQuicConfig(nil)
serverConf.Versions = []protocol.VersionNumber{version}
}
serverConf.Allow0RTT = func(addr net.Addr) bool { return true }
ln, err := quic.ListenAddrEarly(
@ -93,7 +88,7 @@ var _ = Describe("0-RTT", func() {
conn, err := quic.DialAddr(
fmt.Sprintf("localhost:%d", proxy.LocalPort()),
clientConf,
getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
Eventually(puts).Should(Receive())
@ -128,7 +123,7 @@ var _ = Describe("0-RTT", func() {
}()
if clientConf == nil {
clientConf = getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}})
clientConf = getQuicConfig(nil)
}
conn, err := quic.DialAddrEarly(
fmt.Sprintf("localhost:%d", proxyPort),
@ -158,7 +153,7 @@ var _ = Describe("0-RTT", func() {
conn, err := quic.DialAddrEarly(
fmt.Sprintf("localhost:%d", proxyPort),
clientConf,
getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
str, err := conn.OpenUniStream()
@ -202,7 +197,6 @@ var _ = Describe("0-RTT", func() {
"localhost:0",
tlsConf,
getQuicConfig(&quic.Config{
Versions: []protocol.VersionNumber{version},
Allow0RTT: func(addr net.Addr) bool { return true },
Tracer: newTracer(func() logging.ConnectionTracer { return tracer }),
}),
@ -217,10 +211,7 @@ var _ = Describe("0-RTT", func() {
ln,
proxy.LocalPort(),
clientTLSConf,
&quic.Config{
ConnectionIDLength: connIDLen,
Versions: []protocol.VersionNumber{version},
},
&quic.Config{ConnectionIDLength: connIDLen},
PRData,
)
@ -260,7 +251,6 @@ var _ = Describe("0-RTT", func() {
"localhost:0",
tlsConf,
getQuicConfig(&quic.Config{
Versions: []protocol.VersionNumber{version},
Allow0RTT: func(net.Addr) bool { return true },
Tracer: newTracer(func() logging.ConnectionTracer { return tracer }),
}),
@ -292,7 +282,7 @@ var _ = Describe("0-RTT", func() {
conn, err := quic.DialAddrEarly(
fmt.Sprintf("localhost:%d", proxy.LocalPort()),
clientConf,
getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
firstStr, err := conn.OpenUniStream()
@ -342,7 +332,6 @@ var _ = Describe("0-RTT", func() {
"localhost:0",
tlsConf,
getQuicConfig(&quic.Config{
Versions: []protocol.VersionNumber{version},
Allow0RTT: func(net.Addr) bool { return true },
Tracer: newTracer(func() logging.ConnectionTracer { return tracer }),
}),
@ -418,7 +407,6 @@ var _ = Describe("0-RTT", func() {
"localhost:0",
tlsConf,
getQuicConfig(&quic.Config{
Versions: []protocol.VersionNumber{version},
RequireAddressValidation: func(net.Addr) bool { return true },
Allow0RTT: func(net.Addr) bool { return true },
Tracer: newTracer(func() logging.ConnectionTracer { return tracer }),
@ -480,7 +468,6 @@ var _ = Describe("0-RTT", func() {
"localhost:0",
tlsConf,
getQuicConfig(&quic.Config{
Versions: []protocol.VersionNumber{version},
MaxIncomingUniStreams: maxStreams + 1,
Allow0RTT: func(net.Addr) bool { return true },
Tracer: newTracer(func() logging.ConnectionTracer { return tracer }),
@ -494,7 +481,7 @@ var _ = Describe("0-RTT", func() {
conn, err := quic.DialAddrEarly(
fmt.Sprintf("localhost:%d", proxy.LocalPort()),
clientConf,
getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
str, err := conn.OpenUniStream()
@ -525,7 +512,6 @@ var _ = Describe("0-RTT", func() {
"localhost:0",
tlsConf,
getQuicConfig(&quic.Config{
Versions: []protocol.VersionNumber{version},
MaxIncomingStreams: maxStreams - 1,
Allow0RTT: func(net.Addr) bool { return true },
Tracer: newTracer(func() logging.ConnectionTracer { return tracer }),
@ -555,7 +541,6 @@ var _ = Describe("0-RTT", func() {
"localhost:0",
tlsConf,
getQuicConfig(&quic.Config{
Versions: []protocol.VersionNumber{version},
Allow0RTT: func(net.Addr) bool { return true },
Tracer: newTracer(func() logging.ConnectionTracer { return tracer }),
}),
@ -583,7 +568,6 @@ var _ = Describe("0-RTT", func() {
"localhost:0",
tlsConf,
getQuicConfig(&quic.Config{
Versions: []protocol.VersionNumber{version},
Allow0RTT: func(net.Addr) bool { return false }, // application rejects 0-RTT
Tracer: newTracer(func() logging.ConnectionTracer { return tracer }),
}),
@ -605,15 +589,11 @@ var _ = Describe("0-RTT", func() {
DescribeTable("flow control limits",
func(addFlowControlLimit func(*quic.Config, uint64)) {
tracer := newPacketTracer()
firstConf := getQuicConfig(&quic.Config{
Allow0RTT: func(net.Addr) bool { return true },
Versions: []protocol.VersionNumber{version},
})
firstConf := getQuicConfig(&quic.Config{Allow0RTT: func(net.Addr) bool { return true }})
addFlowControlLimit(firstConf, 3)
tlsConf, clientConf := dialAndReceiveSessionTicket(firstConf)
secondConf := getQuicConfig(&quic.Config{
Versions: []protocol.VersionNumber{version},
Allow0RTT: func(net.Addr) bool { return true },
Tracer: newTracer(func() logging.ConnectionTracer { return tracer }),
})
@ -631,7 +611,7 @@ var _ = Describe("0-RTT", func() {
conn, err := quic.DialAddrEarly(
fmt.Sprintf("localhost:%d", proxy.LocalPort()),
clientConf,
getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
str, err := conn.OpenUniStream()
@ -690,7 +670,6 @@ var _ = Describe("0-RTT", func() {
"localhost:0",
tlsConf,
getQuicConfig(&quic.Config{
Versions: []protocol.VersionNumber{version},
MaxIncomingUniStreams: 1,
Tracer: newTracer(func() logging.ConnectionTracer { return tracer }),
}),
@ -703,7 +682,7 @@ var _ = Describe("0-RTT", func() {
conn, err := quic.DialAddrEarly(
fmt.Sprintf("localhost:%d", proxy.LocalPort()),
clientConf,
getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}),
getQuicConfig(nil),
)
Expect(err).ToNot(HaveOccurred())
// The client remembers that it was allowed to open 2 uni-directional streams.
@ -766,7 +745,6 @@ var _ = Describe("0-RTT", func() {
"localhost:0",
tlsConf,
getQuicConfig(&quic.Config{
Versions: []protocol.VersionNumber{version},
Allow0RTT: func(net.Addr) bool { return true },
Tracer: newTracer(func() logging.ConnectionTracer { return tracer }),
}),
@ -792,6 +770,4 @@ var _ = Describe("0-RTT", func() {
Expect(len(zeroRTTPackets)).To(BeNumerically(">", 10))
Expect(zeroRTTPackets[0]).To(Equal(protocol.PacketNumber(0)))
})
})
}
})

View file

@ -0,0 +1,120 @@
package tools
import (
"crypto"
"crypto/ed25519"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"math/big"
"time"
)
const ALPN = "quic-go integration tests"
func GenerateCA() (*x509.Certificate, crypto.PrivateKey, error) {
certTempl := &x509.Certificate{
SerialNumber: big.NewInt(2019),
Subject: pkix.Name{},
NotBefore: time.Now(),
NotAfter: time.Now().Add(24 * time.Hour),
IsCA: true,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
BasicConstraintsValid: true,
}
pub, priv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return nil, nil, err
}
caBytes, err := x509.CreateCertificate(rand.Reader, certTempl, certTempl, pub, priv)
if err != nil {
return nil, nil, err
}
ca, err := x509.ParseCertificate(caBytes)
if err != nil {
return nil, nil, err
}
return ca, priv, nil
}
func GenerateLeafCert(ca *x509.Certificate, caPriv crypto.PrivateKey) (*x509.Certificate, crypto.PrivateKey, error) {
certTempl := &x509.Certificate{
SerialNumber: big.NewInt(1),
DNSNames: []string{"localhost"},
NotBefore: time.Now(),
NotAfter: time.Now().Add(24 * time.Hour),
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature,
}
pub, priv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return nil, nil, err
}
certBytes, err := x509.CreateCertificate(rand.Reader, certTempl, ca, pub, caPriv)
if err != nil {
return nil, nil, err
}
cert, err := x509.ParseCertificate(certBytes)
if err != nil {
return nil, nil, err
}
return cert, priv, nil
}
// GenerateTLSConfigWithLongCertChain generates a tls.Config that uses a long certificate chain.
// The Root CA used is the same as for the config returned from getTLSConfig().
func GenerateTLSConfigWithLongCertChain(ca *x509.Certificate, caPrivateKey crypto.PrivateKey) (*tls.Config, error) {
const chainLen = 7
certTempl := &x509.Certificate{
SerialNumber: big.NewInt(2019),
Subject: pkix.Name{},
NotBefore: time.Now(),
NotAfter: time.Now().Add(24 * time.Hour),
IsCA: true,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
BasicConstraintsValid: true,
}
lastCA := ca
lastCAPrivKey := caPrivateKey
privKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
certs := make([]*x509.Certificate, chainLen)
for i := 0; i < chainLen; i++ {
caBytes, err := x509.CreateCertificate(rand.Reader, certTempl, lastCA, &privKey.PublicKey, lastCAPrivKey)
if err != nil {
return nil, err
}
ca, err := x509.ParseCertificate(caBytes)
if err != nil {
return nil, err
}
certs[i] = ca
lastCA = ca
lastCAPrivKey = privKey
}
leafCert, leafPrivateKey, err := GenerateLeafCert(lastCA, lastCAPrivKey)
if err != nil {
return nil, err
}
rawCerts := make([][]byte, chainLen+1)
for i, cert := range certs {
rawCerts[chainLen-i] = cert.Raw
}
rawCerts[0] = leafCert.Raw
return &tls.Config{
Certificates: []tls.Certificate{{
Certificate: rawCerts,
PrivateKey: leafPrivateKey,
}},
NextProtos: []string{ALPN},
}, nil
}

View file

@ -0,0 +1,31 @@
package tools
import (
"bufio"
"fmt"
"io"
"log"
"os"
"github.com/quic-go/quic-go/internal/utils"
"github.com/quic-go/quic-go/logging"
"github.com/quic-go/quic-go/qlog"
)
func NewQlogger(logger io.Writer) logging.Tracer {
return qlog.NewTracer(func(p logging.Perspective, connectionID []byte) io.WriteCloser {
role := "server"
if p == logging.PerspectiveClient {
role = "client"
}
filename := fmt.Sprintf("log_%x_%s.qlog", connectionID, role)
fmt.Fprintf(logger, "Creating %s.\n", filename)
f, err := os.Create(filename)
if err != nil {
log.Fatalf("failed to create qlog file: %s", err)
return nil
}
bw := bufio.NewWriter(f)
return utils.NewBufferedWriteCloser(bw, f)
})
}

View file

@ -0,0 +1,141 @@
package versionnegotiation
import (
"context"
"crypto/tls"
"fmt"
"net"
"github.com/quic-go/quic-go"
"github.com/quic-go/quic-go/integrationtests/tools/israce"
"github.com/quic-go/quic-go/internal/protocol"
"github.com/quic-go/quic-go/logging"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
type versioner interface {
GetVersion() protocol.VersionNumber
}
type versionNegotiationTracer struct {
logging.NullConnectionTracer
loggedVersions bool
receivedVersionNegotiation bool
chosen logging.VersionNumber
clientVersions, serverVersions []logging.VersionNumber
}
var _ logging.ConnectionTracer = &versionNegotiationTracer{}
func (t *versionNegotiationTracer) NegotiatedVersion(chosen logging.VersionNumber, clientVersions, serverVersions []logging.VersionNumber) {
if t.loggedVersions {
Fail("only expected one call to NegotiatedVersions")
}
t.loggedVersions = true
t.chosen = chosen
t.clientVersions = clientVersions
t.serverVersions = serverVersions
}
func (t *versionNegotiationTracer) ReceivedVersionNegotiationPacket(dest, src logging.ArbitraryLenConnectionID, _ []logging.VersionNumber) {
t.receivedVersionNegotiation = true
}
var _ = Describe("Handshake tests", func() {
startServer := func(tlsConf *tls.Config, conf *quic.Config) (quic.Listener, func()) {
server, err := quic.ListenAddr("localhost:0", tlsConf, conf)
Expect(err).ToNot(HaveOccurred())
acceptStopped := make(chan struct{})
go func() {
defer GinkgoRecover()
defer close(acceptStopped)
for {
if _, err := server.Accept(context.Background()); err != nil {
return
}
}
}()
return server, func() {
server.Close()
<-acceptStopped
}
}
var supportedVersions []protocol.VersionNumber
BeforeEach(func() {
supportedVersions = protocol.SupportedVersions
protocol.SupportedVersions = append(protocol.SupportedVersions, []protocol.VersionNumber{7, 8, 9, 10}...)
})
AfterEach(func() {
protocol.SupportedVersions = supportedVersions
})
if !israce.Enabled {
It("when the server supports more versions than the client", func() {
expectedVersion := protocol.SupportedVersions[0]
// the server doesn't support the highest supported version, which is the first one the client will try
// but it supports a bunch of versions that the client doesn't speak
serverConfig := &quic.Config{}
serverConfig.Versions = []protocol.VersionNumber{7, 8, protocol.SupportedVersions[0], 9}
serverTracer := &versionNegotiationTracer{}
serverConfig.Tracer = newTracer(func() logging.ConnectionTracer { return serverTracer })
server, cl := startServer(getTLSConfig(), serverConfig)
defer cl()
clientTracer := &versionNegotiationTracer{}
conn, err := quic.DialAddr(
fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
getTLSClientConfig(),
maybeAddQlogTracer(&quic.Config{Tracer: newTracer(func() logging.ConnectionTracer { return clientTracer })}),
)
Expect(err).ToNot(HaveOccurred())
Expect(conn.(versioner).GetVersion()).To(Equal(expectedVersion))
Expect(conn.CloseWithError(0, "")).To(Succeed())
Expect(clientTracer.chosen).To(Equal(expectedVersion))
Expect(clientTracer.receivedVersionNegotiation).To(BeFalse())
Expect(clientTracer.clientVersions).To(Equal(protocol.SupportedVersions))
Expect(clientTracer.serverVersions).To(BeEmpty())
Expect(serverTracer.chosen).To(Equal(expectedVersion))
Expect(serverTracer.serverVersions).To(Equal(serverConfig.Versions))
Expect(serverTracer.clientVersions).To(BeEmpty())
})
It("when the client supports more versions than the server supports", func() {
expectedVersion := protocol.SupportedVersions[0]
// the server doesn't support the highest supported version, which is the first one the client will try
// but it supports a bunch of versions that the client doesn't speak
serverConfig := &quic.Config{}
serverConfig.Versions = supportedVersions
serverTracer := &versionNegotiationTracer{}
serverConfig.Tracer = newTracer(func() logging.ConnectionTracer { return serverTracer })
server, cl := startServer(getTLSConfig(), serverConfig)
defer cl()
clientVersions := []protocol.VersionNumber{7, 8, 9, protocol.SupportedVersions[0], 10}
clientTracer := &versionNegotiationTracer{}
conn, err := quic.DialAddr(
fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
getTLSClientConfig(),
maybeAddQlogTracer(&quic.Config{
Versions: clientVersions,
Tracer: newTracer(func() logging.ConnectionTracer { return clientTracer }),
}),
)
Expect(err).ToNot(HaveOccurred())
Expect(conn.(versioner).GetVersion()).To(Equal(protocol.SupportedVersions[0]))
Expect(conn.CloseWithError(0, "")).To(Succeed())
Expect(clientTracer.chosen).To(Equal(expectedVersion))
Expect(clientTracer.receivedVersionNegotiation).To(BeTrue())
Expect(clientTracer.clientVersions).To(Equal(clientVersions))
Expect(clientTracer.serverVersions).To(ContainElements(supportedVersions)) // may contain greased versions
Expect(serverTracer.chosen).To(Equal(expectedVersion))
Expect(serverTracer.serverVersions).To(Equal(serverConfig.Versions))
Expect(serverTracer.clientVersions).To(BeEmpty())
})
}
})

View file

@ -0,0 +1,53 @@
package versionnegotiation
import (
"time"
"github.com/quic-go/quic-go"
quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy"
"github.com/quic-go/quic-go/internal/protocol"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("Handshake RTT tests", func() {
const rtt = 400 * time.Millisecond
expectDurationInRTTs := func(startTime time.Time, num int) {
testDuration := time.Since(startTime)
rtts := float32(testDuration) / float32(rtt)
Expect(rtts).To(SatisfyAll(
BeNumerically(">=", num),
BeNumerically("<", num+1),
))
}
It("fails when there's no matching version, after 1 RTT", func() {
if len(protocol.SupportedVersions) == 1 {
Skip("Test requires at least 2 supported versions.")
}
serverConfig := &quic.Config{}
serverConfig.Versions = protocol.SupportedVersions[:1]
ln, err := quic.ListenAddr("localhost:0", getTLSConfig(), serverConfig)
Expect(err).ToNot(HaveOccurred())
defer ln.Close()
// start the proxy
proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{
RemoteAddr: ln.Addr().String(),
DelayPacket: func(_ quicproxy.Direction, _ []byte) time.Duration { return rtt / 2 },
})
Expect(err).ToNot(HaveOccurred())
startTime := time.Now()
_, err = quic.DialAddr(
proxy.LocalAddr().String(),
getTLSClientConfig(),
maybeAddQlogTracer(&quic.Config{Versions: protocol.SupportedVersions[1:2]}),
)
Expect(err).To(HaveOccurred())
expectDurationInRTTs(startTime, 1)
})
})

View file

@ -0,0 +1,89 @@
package versionnegotiation
import (
"context"
"crypto/tls"
"crypto/x509"
"flag"
"testing"
"github.com/quic-go/quic-go/integrationtests/tools"
"github.com/quic-go/quic-go/logging"
"github.com/quic-go/quic-go"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var (
enableQlog bool
tlsConfig *tls.Config
tlsClientConfig *tls.Config
)
func init() {
flag.BoolVar(&enableQlog, "qlog", false, "enable qlog")
ca, caPrivateKey, err := tools.GenerateCA()
if err != nil {
panic(err)
}
leafCert, leafPrivateKey, err := tools.GenerateLeafCert(ca, caPrivateKey)
if err != nil {
panic(err)
}
tlsConfig = &tls.Config{
Certificates: []tls.Certificate{{
Certificate: [][]byte{leafCert.Raw},
PrivateKey: leafPrivateKey,
}},
NextProtos: []string{tools.ALPN},
}
root := x509.NewCertPool()
root.AddCert(ca)
tlsClientConfig = &tls.Config{
RootCAs: root,
NextProtos: []string{tools.ALPN},
}
}
func getTLSConfig() *tls.Config { return tlsConfig }
func getTLSClientConfig() *tls.Config { return tlsClientConfig }
func TestQuicVersionNegotiation(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Version Negotiation Suite")
}
func maybeAddQlogTracer(c *quic.Config) *quic.Config {
if c == nil {
c = &quic.Config{}
}
if !enableQlog {
return c
}
qlogger := tools.NewQlogger(GinkgoWriter)
if c.Tracer == nil {
c.Tracer = qlogger
} else if qlogger != nil {
c.Tracer = logging.NewMultiplexedTracer(qlogger, c.Tracer)
}
return c
}
type tracer struct {
logging.NullTracer
createNewConnTracer func() logging.ConnectionTracer
}
var _ logging.Tracer = &tracer{}
func newTracer(c func() logging.ConnectionTracer) logging.Tracer {
return &tracer{createNewConnTracer: c}
}
func (t *tracer) TracerForConnection(context.Context, logging.Perspective, logging.ConnectionID) logging.ConnectionTracer {
return t.createNewConnTracer()
}