Skip to content

Commit

Permalink
send large max_datagram_frame size, introduce a DatagramTooLargeError…
Browse files Browse the repository at this point in the history
… error (quic-go#4143)

The size can be overwritten to a lower value for testing.
  • Loading branch information
chungthuang authored Dec 2, 2023
1 parent 2d7ea37 commit 7b9d21f
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 16 deletions.
10 changes: 6 additions & 4 deletions connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ var newConnection = func(
RetrySourceConnectionID: retrySrcConnID,
}
if s.config.EnableDatagrams {
params.MaxDatagramFrameSize = protocol.MaxDatagramFrameSize
params.MaxDatagramFrameSize = wire.MaxDatagramSize
} else {
params.MaxDatagramFrameSize = protocol.InvalidByteCount
}
Expand Down Expand Up @@ -414,7 +414,7 @@ var newClientConnection = func(
InitialSourceConnectionID: srcConnID,
}
if s.config.EnableDatagrams {
params.MaxDatagramFrameSize = protocol.MaxDatagramFrameSize
params.MaxDatagramFrameSize = wire.MaxDatagramSize
} else {
params.MaxDatagramFrameSize = protocol.InvalidByteCount
}
Expand Down Expand Up @@ -1522,7 +1522,7 @@ func (s *connection) handleAckFrame(frame *wire.AckFrame, encLevel protocol.Encr
}

func (s *connection) handleDatagramFrame(f *wire.DatagramFrame) error {
if f.Length(s.version) > protocol.MaxDatagramFrameSize {
if f.Length(s.version) > wire.MaxDatagramSize {
return &qerr.TransportError{
ErrorCode: qerr.ProtocolViolation,
ErrorMessage: "DATAGRAM frame too large",
Expand Down Expand Up @@ -2350,7 +2350,9 @@ func (s *connection) SendDatagram(p []byte) error {

f := &wire.DatagramFrame{DataLenPresent: true}
if protocol.ByteCount(len(p)) > f.MaxDataLen(s.peerParams.MaxDatagramFrameSize, s.version) {
return errors.New("message too large")
return &DatagramTooLargeError{
PeerMaxDatagramFrameSize: int64(s.peerParams.MaxDatagramFrameSize),
}
}
f.Data = make([]byte, len(p))
copy(f.Data, p)
Expand Down
12 changes: 12 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,15 @@ func (e *StreamError) Error() string {
}
return fmt.Sprintf("stream %d canceled by %s with error code %d", e.StreamID, pers, e.ErrorCode)
}

// DatagramTooLargeError is returned from Connection.SendDatagram if the payload is too large to be sent.
type DatagramTooLargeError struct {
PeerMaxDatagramFrameSize int64
}

func (e *DatagramTooLargeError) Is(target error) bool {
_, ok := target.(*DatagramTooLargeError)
return ok
}

func (e *DatagramTooLargeError) Error() string { return "DATAGRAM frame too large" }
23 changes: 16 additions & 7 deletions integrationtests/self/datagram_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import (
)

var _ = Describe("Datagram test", func() {
const num = 100
const concurrentSends = 100
const maxDatagramSize = 250

var (
serverConn, clientConn *net.UDPConn
Expand Down Expand Up @@ -47,11 +48,11 @@ var _ = Describe("Datagram test", func() {

if expectDatagramSupport {
Expect(conn.ConnectionState().SupportsDatagrams).To(BeTrue())

if enableDatagram {
f := &wire.DatagramFrame{DataLenPresent: true}
var wg sync.WaitGroup
wg.Add(num)
for i := 0; i < num; i++ {
wg.Add(concurrentSends)
for i := 0; i < concurrentSends; i++ {
go func(i int) {
defer GinkgoRecover()
defer wg.Done()
Expand All @@ -60,6 +61,11 @@ var _ = Describe("Datagram test", func() {
Expect(conn.SendDatagram(b)).To(Succeed())
}(i)
}
maxDatagramMessageSize := f.MaxDataLen(maxDatagramSize, conn.ConnectionState().Version)
b := make([]byte, maxDatagramMessageSize+1)
Expect(conn.SendDatagram(b)).To(MatchError(&quic.DatagramTooLargeError{
PeerMaxDatagramFrameSize: int64(maxDatagramMessageSize),
}))
wg.Wait()
}
} else {
Expand Down Expand Up @@ -103,6 +109,8 @@ var _ = Describe("Datagram test", func() {
})

It("sends datagrams", func() {
oldMaxDatagramSize := wire.MaxDatagramSize
wire.MaxDatagramSize = maxDatagramSize
proxyPort, close := startServerAndProxy(true, true)
defer close()
raddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", proxyPort))
Expand All @@ -128,14 +136,15 @@ var _ = Describe("Datagram test", func() {
}

numDropped := int(dropped.Load())
expVal := num - numDropped
expVal := concurrentSends - numDropped
fmt.Fprintf(GinkgoWriter, "Dropped %d out of %d packets.\n", numDropped, total.Load())
fmt.Fprintf(GinkgoWriter, "Received %d out of %d sent datagrams.\n", counter, num)
fmt.Fprintf(GinkgoWriter, "Received %d out of %d sent datagrams.\n", counter, concurrentSends)
Expect(counter).To(And(
BeNumerically(">", expVal*9/10),
BeNumerically("<", num),
BeNumerically("<", concurrentSends),
))
Eventually(conn.Context().Done).Should(BeClosed())
wire.MaxDatagramSize = oldMaxDatagramSize
})

It("server can disable datagram", func() {
Expand Down
4 changes: 0 additions & 4 deletions internal/protocol/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,6 @@ const MaxPostHandshakeCryptoFrameSize = 1000
// but must ensure that a maximum size ACK frame fits into one packet.
const MaxAckFrameSize ByteCount = 1000

// MaxDatagramFrameSize is the maximum size of a DATAGRAM frame (RFC 9221).
// The size is chosen such that a DATAGRAM frame fits into a QUIC packet.
const MaxDatagramFrameSize ByteCount = 1200

// DatagramRcvQueueLen is the length of the receive queue for DATAGRAM frames (RFC 9221)
const DatagramRcvQueueLen = 128

Expand Down
6 changes: 6 additions & 0 deletions internal/wire/datagram_frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ import (
"github.com/quic-go/quic-go/quicvarint"
)

// MaxDatagramSize is the maximum size of a DATAGRAM frame (RFC 9221).
// By setting it to a large value, we allow all datagrams that fit into a QUIC packet.
// The value is chosen such that it can still be encoded as a 2 byte varint.
// This is a var and not a const so it can be set in tests.
var MaxDatagramSize protocol.ByteCount = 16383

// A DatagramFrame is a DATAGRAM frame
type DatagramFrame struct {
DataLenPresent bool
Expand Down
2 changes: 1 addition & 1 deletion internal/wire/transport_parameter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ var _ = Describe("Transport Parameters", func() {
MaxBidiStreamNum: protocol.StreamNum(getRandomValueUpTo(int64(protocol.MaxStreamCount))),
MaxUniStreamNum: protocol.StreamNum(getRandomValueUpTo(int64(protocol.MaxStreamCount))),
ActiveConnectionIDLimit: 2 + getRandomValueUpTo(math.MaxInt64-2),
MaxDatagramFrameSize: protocol.ByteCount(getRandomValueUpTo(int64(protocol.MaxDatagramFrameSize))),
MaxDatagramFrameSize: protocol.ByteCount(getRandomValueUpTo(int64(MaxDatagramSize))),
}
Expect(params.ValidFor0RTT(params)).To(BeTrue())
b := params.MarshalForSessionTicket(nil)
Expand Down

0 comments on commit 7b9d21f

Please sign in to comment.