mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-03 20:27:35 +03:00
implement the Transport
This commit is contained in:
parent
ae5a8bd35c
commit
8189e75be6
31 changed files with 1309 additions and 1250 deletions
287
transport_test.go
Normal file
287
transport_test.go
Normal file
|
@ -0,0 +1,287 @@
|
|||
package quic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
mocklogging "github.com/quic-go/quic-go/internal/mocks/logging"
|
||||
"github.com/quic-go/quic-go/internal/protocol"
|
||||
"github.com/quic-go/quic-go/internal/wire"
|
||||
"github.com/quic-go/quic-go/logging"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Transport", func() {
|
||||
type packetToRead struct {
|
||||
addr net.Addr
|
||||
data []byte
|
||||
err error
|
||||
}
|
||||
|
||||
getPacketWithPacketType := func(connID protocol.ConnectionID, t protocol.PacketType, length protocol.ByteCount) []byte {
|
||||
b, err := (&wire.ExtendedHeader{
|
||||
Header: wire.Header{
|
||||
Type: t,
|
||||
DestConnectionID: connID,
|
||||
Length: length,
|
||||
Version: protocol.Version1,
|
||||
},
|
||||
PacketNumberLen: protocol.PacketNumberLen2,
|
||||
}).Append(nil, protocol.Version1)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
return b
|
||||
}
|
||||
|
||||
getPacket := func(connID protocol.ConnectionID) []byte {
|
||||
return getPacketWithPacketType(connID, protocol.PacketTypeHandshake, 2)
|
||||
}
|
||||
|
||||
newMockPacketConn := func(packetChan <-chan packetToRead) *MockPacketConn {
|
||||
conn := NewMockPacketConn(mockCtrl)
|
||||
conn.EXPECT().LocalAddr().Return(&net.UDPAddr{}).AnyTimes()
|
||||
conn.EXPECT().ReadFrom(gomock.Any()).DoAndReturn(func(b []byte) (int, net.Addr, error) {
|
||||
p, ok := <-packetChan
|
||||
if !ok {
|
||||
return 0, nil, errors.New("closed")
|
||||
}
|
||||
return copy(b, p.data), p.addr, p.err
|
||||
}).AnyTimes()
|
||||
// for shutdown
|
||||
conn.EXPECT().SetReadDeadline(gomock.Any()).AnyTimes()
|
||||
return conn
|
||||
}
|
||||
|
||||
It("handles packets for different packet handlers on the same packet conn", func() {
|
||||
packetChan := make(chan packetToRead)
|
||||
tr := &Transport{Conn: newMockPacketConn(packetChan)}
|
||||
tr.init(&Config{})
|
||||
phm := NewMockPacketHandlerManager(mockCtrl)
|
||||
tr.handlerMap = phm
|
||||
connID1 := protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8})
|
||||
connID2 := protocol.ParseConnectionID([]byte{8, 7, 6, 5, 4, 3, 2, 1})
|
||||
|
||||
handled := make(chan struct{}, 2)
|
||||
phm.EXPECT().Get(connID1).DoAndReturn(func(protocol.ConnectionID) (packetHandler, bool) {
|
||||
h := NewMockPacketHandler(mockCtrl)
|
||||
h.EXPECT().handlePacket(gomock.Any()).Do(func(p *receivedPacket) {
|
||||
defer GinkgoRecover()
|
||||
connID, err := wire.ParseConnectionID(p.data, 0)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(connID).To(Equal(connID1))
|
||||
handled <- struct{}{}
|
||||
})
|
||||
return h, true
|
||||
})
|
||||
phm.EXPECT().Get(connID2).DoAndReturn(func(protocol.ConnectionID) (packetHandler, bool) {
|
||||
h := NewMockPacketHandler(mockCtrl)
|
||||
h.EXPECT().handlePacket(gomock.Any()).Do(func(p *receivedPacket) {
|
||||
defer GinkgoRecover()
|
||||
connID, err := wire.ParseConnectionID(p.data, 0)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(connID).To(Equal(connID2))
|
||||
handled <- struct{}{}
|
||||
})
|
||||
return h, true
|
||||
})
|
||||
|
||||
packetChan <- packetToRead{data: getPacket(connID1)}
|
||||
packetChan <- packetToRead{data: getPacket(connID2)}
|
||||
|
||||
Eventually(handled).Should(Receive())
|
||||
Eventually(handled).Should(Receive())
|
||||
|
||||
// shutdown
|
||||
phm.EXPECT().Close(gomock.Any())
|
||||
close(packetChan)
|
||||
tr.Close()
|
||||
})
|
||||
|
||||
It("closes listeners", func() {
|
||||
packetChan := make(chan packetToRead)
|
||||
tr := &Transport{Conn: newMockPacketConn(packetChan)}
|
||||
defer tr.Close()
|
||||
ln, err := tr.Listen(&tls.Config{}, nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
phm := NewMockPacketHandlerManager(mockCtrl)
|
||||
tr.handlerMap = phm
|
||||
|
||||
phm.EXPECT().CloseServer()
|
||||
Expect(ln.Close()).To(Succeed())
|
||||
|
||||
// shutdown
|
||||
phm.EXPECT().Close(gomock.Any())
|
||||
close(packetChan)
|
||||
tr.Close()
|
||||
})
|
||||
|
||||
It("drops unparseable packets", func() {
|
||||
addr := &net.UDPAddr{IP: net.IPv4(9, 8, 7, 6), Port: 1234}
|
||||
packetChan := make(chan packetToRead)
|
||||
tracer := mocklogging.NewMockTracer(mockCtrl)
|
||||
tr := &Transport{
|
||||
Conn: newMockPacketConn(packetChan),
|
||||
}
|
||||
tr.init(&Config{Tracer: tracer, ConnectionIDLength: 10})
|
||||
dropped := make(chan struct{})
|
||||
tracer.EXPECT().DroppedPacket(addr, logging.PacketTypeNotDetermined, protocol.ByteCount(4), logging.PacketDropHeaderParseError).Do(func(net.Addr, logging.PacketType, protocol.ByteCount, logging.PacketDropReason) { close(dropped) })
|
||||
packetChan <- packetToRead{
|
||||
addr: addr,
|
||||
data: []byte{0, 1, 2, 3},
|
||||
}
|
||||
Eventually(dropped).Should(BeClosed())
|
||||
|
||||
// shutdown
|
||||
close(packetChan)
|
||||
tr.Close()
|
||||
})
|
||||
|
||||
It("closes when reading from the conn fails", func() {
|
||||
packetChan := make(chan packetToRead)
|
||||
tr := Transport{Conn: newMockPacketConn(packetChan)}
|
||||
defer tr.Close()
|
||||
phm := NewMockPacketHandlerManager(mockCtrl)
|
||||
tr.init(&Config{})
|
||||
tr.handlerMap = phm
|
||||
|
||||
done := make(chan struct{})
|
||||
phm.EXPECT().Close(gomock.Any()).Do(func(error) { close(done) })
|
||||
packetChan <- packetToRead{err: errors.New("read failed")}
|
||||
Eventually(done).Should(BeClosed())
|
||||
|
||||
// shutdown
|
||||
close(packetChan)
|
||||
tr.Close()
|
||||
})
|
||||
|
||||
It("continues listening after temporary errors", func() {
|
||||
packetChan := make(chan packetToRead)
|
||||
tr := Transport{Conn: newMockPacketConn(packetChan)}
|
||||
defer tr.Close()
|
||||
phm := NewMockPacketHandlerManager(mockCtrl)
|
||||
tr.init(&Config{})
|
||||
tr.handlerMap = phm
|
||||
|
||||
tempErr := deadlineError{}
|
||||
Expect(tempErr.Temporary()).To(BeTrue())
|
||||
packetChan <- packetToRead{err: tempErr}
|
||||
// don't expect any calls to phm.Close
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
// shutdown
|
||||
phm.EXPECT().Close(gomock.Any())
|
||||
close(packetChan)
|
||||
tr.Close()
|
||||
})
|
||||
|
||||
It("handles short header packets resets", func() {
|
||||
connID := protocol.ParseConnectionID([]byte{2, 3, 4, 5})
|
||||
packetChan := make(chan packetToRead)
|
||||
tr := Transport{Conn: newMockPacketConn(packetChan)}
|
||||
tr.init(&Config{ConnectionIDLength: connID.Len()})
|
||||
defer tr.Close()
|
||||
phm := NewMockPacketHandlerManager(mockCtrl)
|
||||
tr.init(&Config{})
|
||||
tr.handlerMap = phm
|
||||
|
||||
var token protocol.StatelessResetToken
|
||||
rand.Read(token[:])
|
||||
|
||||
var b []byte
|
||||
b, err := wire.AppendShortHeader(b, connID, 1337, 2, protocol.KeyPhaseOne)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
b = append(b, token[:]...)
|
||||
conn := NewMockPacketHandler(mockCtrl)
|
||||
gomock.InOrder(
|
||||
phm.EXPECT().GetByResetToken(token),
|
||||
phm.EXPECT().Get(connID).Return(conn, true),
|
||||
conn.EXPECT().handlePacket(gomock.Any()).Do(func(p *receivedPacket) {
|
||||
Expect(p.data).To(Equal(b))
|
||||
Expect(p.rcvTime).To(BeTemporally("~", time.Now(), time.Second))
|
||||
}),
|
||||
)
|
||||
packetChan <- packetToRead{data: b}
|
||||
|
||||
// shutdown
|
||||
phm.EXPECT().Close(gomock.Any())
|
||||
close(packetChan)
|
||||
tr.Close()
|
||||
})
|
||||
|
||||
It("handles stateless resets", func() {
|
||||
connID := protocol.ParseConnectionID([]byte{2, 3, 4, 5})
|
||||
packetChan := make(chan packetToRead)
|
||||
tr := Transport{Conn: newMockPacketConn(packetChan)}
|
||||
tr.init(&Config{ConnectionIDLength: connID.Len()})
|
||||
defer tr.Close()
|
||||
phm := NewMockPacketHandlerManager(mockCtrl)
|
||||
tr.init(&Config{})
|
||||
tr.handlerMap = phm
|
||||
|
||||
var token protocol.StatelessResetToken
|
||||
rand.Read(token[:])
|
||||
|
||||
var b []byte
|
||||
b, err := wire.AppendShortHeader(b, connID, 1337, 2, protocol.KeyPhaseOne)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
b = append(b, token[:]...)
|
||||
conn := NewMockPacketHandler(mockCtrl)
|
||||
gomock.InOrder(
|
||||
phm.EXPECT().GetByResetToken(token).Return(conn, true),
|
||||
conn.EXPECT().destroy(gomock.Any()).Do(func(err error) {
|
||||
Expect(err).To(MatchError(&StatelessResetError{Token: token}))
|
||||
}),
|
||||
)
|
||||
packetChan <- packetToRead{data: b}
|
||||
|
||||
// shutdown
|
||||
phm.EXPECT().Close(gomock.Any())
|
||||
close(packetChan)
|
||||
tr.Close()
|
||||
})
|
||||
|
||||
It("sends stateless resets", func() {
|
||||
connID := protocol.ParseConnectionID([]byte{2, 3, 4, 5})
|
||||
packetChan := make(chan packetToRead)
|
||||
conn := newMockPacketConn(packetChan)
|
||||
tr := Transport{
|
||||
Conn: conn,
|
||||
}
|
||||
tr.init(&Config{ConnectionIDLength: connID.Len(), StatelessResetKey: &StatelessResetKey{1, 2, 3, 4}})
|
||||
defer tr.Close()
|
||||
phm := NewMockPacketHandlerManager(mockCtrl)
|
||||
tr.init(&Config{})
|
||||
tr.handlerMap = phm
|
||||
|
||||
var b []byte
|
||||
b, err := wire.AppendShortHeader(b, connID, 1337, 2, protocol.KeyPhaseOne)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
b = append(b, make([]byte, protocol.MinStatelessResetSize-len(b)+1)...)
|
||||
|
||||
var token protocol.StatelessResetToken
|
||||
rand.Read(token[:])
|
||||
written := make(chan struct{})
|
||||
gomock.InOrder(
|
||||
phm.EXPECT().GetByResetToken(gomock.Any()),
|
||||
phm.EXPECT().Get(connID),
|
||||
phm.EXPECT().GetStatelessResetToken(connID).Return(token),
|
||||
conn.EXPECT().WriteTo(gomock.Any(), gomock.Any()).Do(func(b []byte, _ net.Addr) {
|
||||
defer close(written)
|
||||
Expect(bytes.Contains(b, token[:])).To(BeTrue())
|
||||
}),
|
||||
)
|
||||
packetChan <- packetToRead{data: b}
|
||||
Eventually(written).Should(BeClosed())
|
||||
|
||||
// shutdown
|
||||
phm.EXPECT().Close(gomock.Any())
|
||||
close(packetChan)
|
||||
tr.Close()
|
||||
})
|
||||
})
|
Loading…
Add table
Add a link
Reference in a new issue