1package benchmark
2
3import (
4	"bytes"
5	"context"
6	"crypto/tls"
7	"fmt"
8	"io"
9	"math/rand"
10	"net"
11
12	quic "github.com/lucas-clemente/quic-go"
13	"github.com/lucas-clemente/quic-go/internal/protocol"
14	"github.com/lucas-clemente/quic-go/internal/testdata"
15
16	. "github.com/onsi/ginkgo"
17	. "github.com/onsi/gomega"
18)
19
20var _ = Describe("Benchmarks", func() {
21	for i := range protocol.SupportedVersions {
22		version := protocol.SupportedVersions[i]
23
24		Context(fmt.Sprintf("with version %s", version), func() {
25			var data []byte
26			var dataLen int
27
28			BeforeEach(func() {
29				dataLen = size * /* MB */ 1e6
30				data = make([]byte, dataLen)
31				rand.Read(data) // no need to check for an error. math.Rand.Read never errors
32			})
33
34			Measure("transferring a file", func(b Benchmarker) {
35				var ln quic.Listener
36				serverAddr := make(chan net.Addr)
37				handshakeChan := make(chan struct{})
38				// start the server
39				go func() {
40					defer GinkgoRecover()
41					var err error
42					tlsConf := testdata.GetTLSConfig()
43					tlsConf.NextProtos = []string{"benchmark"}
44					ln, err = quic.ListenAddr(
45						"localhost:0",
46						tlsConf,
47						&quic.Config{Versions: []protocol.VersionNumber{version}},
48					)
49					Expect(err).ToNot(HaveOccurred())
50					serverAddr <- ln.Addr()
51					sess, err := ln.Accept(context.Background())
52					Expect(err).ToNot(HaveOccurred())
53					// wait for the client to complete the handshake before sending the data
54					// this should not be necessary, but due to timing issues on the CIs, this is necessary to avoid sending too many undecryptable packets
55					<-handshakeChan
56					str, err := sess.OpenStream()
57					Expect(err).ToNot(HaveOccurred())
58					_, err = str.Write(data)
59					Expect(err).ToNot(HaveOccurred())
60					err = str.Close()
61					Expect(err).ToNot(HaveOccurred())
62				}()
63
64				// start the client
65				addr := <-serverAddr
66				sess, err := quic.DialAddr(
67					addr.String(),
68					&tls.Config{InsecureSkipVerify: true, NextProtos: []string{"benchmark"}},
69					&quic.Config{Versions: []protocol.VersionNumber{version}},
70				)
71				Expect(err).ToNot(HaveOccurred())
72				close(handshakeChan)
73				str, err := sess.AcceptStream(context.Background())
74				Expect(err).ToNot(HaveOccurred())
75
76				buf := &bytes.Buffer{}
77				// measure the time it takes to download the dataLen bytes
78				// note we're measuring the time for the transfer, i.e. excluding the handshake
79				runtime := b.Time("transfer time", func() {
80					_, err := io.Copy(buf, str)
81					Expect(err).NotTo(HaveOccurred())
82				})
83				Expect(buf.Bytes()).To(Equal(data))
84
85				b.RecordValue("transfer rate [MB/s]", float64(dataLen)/1e6/runtime.Seconds())
86
87				ln.Close()
88				sess.CloseWithError(0, "")
89			}, 3)
90		})
91	}
92})
93