mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-06 13:47:35 +03:00
implement a quic.Listener, privatize the Server
This commit is contained in:
parent
6dd163020a
commit
5029ab0934
6 changed files with 221 additions and 139 deletions
|
@ -11,6 +11,7 @@ import (
|
|||
)
|
||||
|
||||
type mockPacketConn struct {
|
||||
addr net.Addr
|
||||
dataToRead []byte
|
||||
dataReadFrom net.Addr
|
||||
dataWritten bytes.Buffer
|
||||
|
@ -32,7 +33,7 @@ func (c *mockPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
|
|||
return c.dataWritten.Write(b)
|
||||
}
|
||||
func (c *mockPacketConn) Close() error { c.closed = true; return nil }
|
||||
func (c *mockPacketConn) LocalAddr() net.Addr { panic("not implemented") }
|
||||
func (c *mockPacketConn) LocalAddr() net.Addr { return c.addr }
|
||||
func (c *mockPacketConn) SetDeadline(t time.Time) error { panic("not implemented") }
|
||||
func (c *mockPacketConn) SetReadDeadline(t time.Time) error { panic("not implemented") }
|
||||
func (c *mockPacketConn) SetWriteDeadline(t time.Time) error { panic("not implemented") }
|
||||
|
|
|
@ -20,10 +20,8 @@ import (
|
|||
)
|
||||
|
||||
type streamCreator interface {
|
||||
AcceptStream() (utils.Stream, error)
|
||||
quic.Session
|
||||
GetOrOpenStream(protocol.StreamID) (utils.Stream, error)
|
||||
Close(error) error
|
||||
RemoteAddr() net.Addr
|
||||
}
|
||||
|
||||
// Server is a HTTP2 server listening for QUIC connections.
|
||||
|
@ -35,8 +33,8 @@ type Server struct {
|
|||
|
||||
port uint32 // used atomically
|
||||
|
||||
server *quic.Server
|
||||
serverMutex sync.Mutex
|
||||
listenerMutex sync.Mutex
|
||||
listener quic.Listener
|
||||
}
|
||||
|
||||
// ListenAndServe listens on the UDP address s.Addr and calls s.Handler to handle HTTP/2 requests on incoming connections.
|
||||
|
@ -72,31 +70,41 @@ func (s *Server) serveImpl(tlsConfig *tls.Config, conn *net.UDPConn) error {
|
|||
if s.Server == nil {
|
||||
return errors.New("use of h2quic.Server without http.Server")
|
||||
}
|
||||
s.serverMutex.Lock()
|
||||
if s.server != nil {
|
||||
s.serverMutex.Unlock()
|
||||
s.listenerMutex.Lock()
|
||||
if s.listener != nil {
|
||||
s.listenerMutex.Unlock()
|
||||
return errors.New("ListenAndServe may only be called once")
|
||||
}
|
||||
var err error
|
||||
server, err := quic.NewServer(s.Addr, tlsConfig, s.handleStreamCb)
|
||||
config := quic.Config{
|
||||
TLSConfig: tlsConfig,
|
||||
ConnState: func(session quic.Session, connState quic.ConnState) {
|
||||
sess := session.(streamCreator)
|
||||
if connState == quic.ConnStateVersionNegotiated {
|
||||
s.handleHeaderStream(sess)
|
||||
}
|
||||
},
|
||||
}
|
||||
ln, err := quic.NewListener(&config)
|
||||
if err != nil {
|
||||
s.serverMutex.Unlock()
|
||||
s.listenerMutex.Unlock()
|
||||
return err
|
||||
}
|
||||
s.server = server
|
||||
s.serverMutex.Unlock()
|
||||
s.listener = ln
|
||||
s.listenerMutex.Unlock()
|
||||
if conn == nil {
|
||||
return server.ListenAndServe()
|
||||
return ln.ListenAddr(s.Addr)
|
||||
}
|
||||
return server.Serve(conn)
|
||||
return ln.Listen(conn)
|
||||
}
|
||||
|
||||
func (s *Server) handleStreamCb(session quic.Session, stream utils.Stream) {
|
||||
s.handleStream(session.(streamCreator), stream)
|
||||
}
|
||||
|
||||
func (s *Server) handleStream(session streamCreator, stream utils.Stream) {
|
||||
func (s *Server) handleHeaderStream(session streamCreator) {
|
||||
stream, err := session.AcceptStream()
|
||||
if err != nil {
|
||||
session.Close(qerr.Error(qerr.InvalidHeadersStreamData, err.Error()))
|
||||
return
|
||||
}
|
||||
if stream.StreamID() != 3 {
|
||||
session.Close(qerr.Error(qerr.InternalError, "h2quic server BUG: header stream does not have stream ID 3"))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -214,11 +222,11 @@ func (s *Server) handleRequest(session streamCreator, headerStream utils.Stream,
|
|||
// Close the server immediately, aborting requests and sending CONNECTION_CLOSE frames to connected clients.
|
||||
// Close in combination with ListenAndServe() (instead of Serve()) may race if it is called before a UDP socket is established.
|
||||
func (s *Server) Close() error {
|
||||
s.serverMutex.Lock()
|
||||
defer s.serverMutex.Unlock()
|
||||
if s.server != nil {
|
||||
err := s.server.Close()
|
||||
s.server = nil
|
||||
s.listenerMutex.Lock()
|
||||
defer s.listenerMutex.Unlock()
|
||||
if s.listener != nil {
|
||||
err := s.listener.Close()
|
||||
s.listener = nil
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -26,12 +26,16 @@ type mockSession struct {
|
|||
closed bool
|
||||
closedWithError error
|
||||
dataStream utils.Stream
|
||||
streamToAccept utils.Stream
|
||||
}
|
||||
|
||||
func (s *mockSession) GetOrOpenStream(id protocol.StreamID) (utils.Stream, error) {
|
||||
return s.dataStream, nil
|
||||
}
|
||||
func (s *mockSession) AcceptStream() (utils.Stream, error) {
|
||||
return s.streamToAccept, nil
|
||||
}
|
||||
func (s *mockSession) OpenStream() (utils.Stream, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
func (s *mockSession) Close(e error) error {
|
||||
|
@ -230,7 +234,8 @@ var _ = Describe("H2 server", func() {
|
|||
// Taken from https://http2.github.io/http2-spec/compression.html#request.examples.with.huffman.coding
|
||||
0x82, 0x86, 0x84, 0x41, 0x8c, 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff,
|
||||
})
|
||||
s.handleStream(session, headerStream)
|
||||
session.streamToAccept = headerStream
|
||||
go s.handleHeaderStream(session)
|
||||
Eventually(func() bool { return handlerCalled }).Should(BeTrue())
|
||||
})
|
||||
|
||||
|
@ -241,26 +246,24 @@ var _ = Describe("H2 server", func() {
|
|||
})
|
||||
headerStream := &mockStream{id: 3}
|
||||
headerStream.dataToRead.Write(bytes.Repeat([]byte{0}, 100))
|
||||
s.handleStream(session, headerStream)
|
||||
session.streamToAccept = headerStream
|
||||
go s.handleHeaderStream(session)
|
||||
Consistently(func() bool { return handlerCalled }).Should(BeFalse())
|
||||
Eventually(func() bool { return session.closed }).Should(BeTrue())
|
||||
Expect(session.closedWithError).To(MatchError(qerr.Error(qerr.InvalidHeadersStreamData, "connection error: PROTOCOL_ERROR")))
|
||||
})
|
||||
|
||||
It("ignores other streams", func() {
|
||||
var handlerCalled bool
|
||||
s.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
Expect(r.Host).To(Equal("www.example.com"))
|
||||
handlerCalled = true
|
||||
})
|
||||
headerStream := &mockStream{id: 5}
|
||||
It("errors if the accepted header stream has the wrong stream ID", func() {
|
||||
headerStream := &mockStream{id: 1}
|
||||
headerStream.dataToRead.Write([]byte{
|
||||
0x0, 0x0, 0x11, 0x1, 0x4, 0x0, 0x0, 0x0, 0x5,
|
||||
// Taken from https://http2.github.io/http2-spec/compression.html#request.examples.with.huffman.coding
|
||||
0x82, 0x86, 0x84, 0x41, 0x8c, 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff,
|
||||
})
|
||||
s.handleStream(session, headerStream)
|
||||
Consistently(func() bool { return handlerCalled }).Should(BeFalse())
|
||||
session.streamToAccept = headerStream
|
||||
go s.handleHeaderStream(session)
|
||||
Eventually(func() bool { return session.closed }).Should(BeTrue())
|
||||
Expect(session.closedWithError).To(MatchError(qerr.Error(qerr.InternalError, "h2quic server BUG: header stream does not have stream ID 3")))
|
||||
})
|
||||
|
||||
It("supports closing after first request", func() {
|
||||
|
@ -272,8 +275,9 @@ var _ = Describe("H2 server", func() {
|
|||
// Taken from https://http2.github.io/http2-spec/compression.html#request.examples.with.huffman.coding
|
||||
0x82, 0x86, 0x84, 0x41, 0x8c, 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff,
|
||||
})
|
||||
session.streamToAccept = headerStream
|
||||
Expect(session.closed).To(BeFalse())
|
||||
s.handleStream(session, headerStream)
|
||||
go s.handleHeaderStream(session)
|
||||
Eventually(func() bool { return session.closed }).Should(BeTrue())
|
||||
})
|
||||
|
||||
|
@ -289,7 +293,8 @@ var _ = Describe("H2 server", func() {
|
|||
// Taken from https://http2.github.io/http2-spec/compression.html#request.examples.with.huffman.coding
|
||||
0x82, 0x86, 0x84, 0x41, 0x8c, 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff,
|
||||
})
|
||||
s.handleStream(session, headerStream)
|
||||
session.streamToAccept = headerStream
|
||||
go s.handleHeaderStream(session)
|
||||
Eventually(func() bool { return handlerCalled }).Should(BeTrue())
|
||||
})
|
||||
|
||||
|
|
32
interface.go
32
interface.go
|
@ -1,6 +1,7 @@
|
|||
package quic
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/utils"
|
||||
|
@ -20,3 +21,34 @@ type Session interface {
|
|||
RemoteAddr() net.Addr
|
||||
Close(error) error
|
||||
}
|
||||
|
||||
// ConnState is the status of the connection
|
||||
type ConnState int
|
||||
|
||||
const (
|
||||
// ConnStateVersionNegotiated means that version negotiation is complete
|
||||
ConnStateVersionNegotiated ConnState = iota
|
||||
// ConnStateSecure means that the connection is encrypted
|
||||
ConnStateSecure
|
||||
// ConnStateForwardSecure means that the connection is forward secure
|
||||
ConnStateForwardSecure
|
||||
)
|
||||
|
||||
// ConnStateCallback is called every time the connection moves to another connection state
|
||||
// the callback is called in a new go routine
|
||||
type ConnStateCallback func(Session, ConnState)
|
||||
|
||||
// Config is the configuration for QUIC
|
||||
type Config struct {
|
||||
TLSConfig *tls.Config
|
||||
// will be called in a separate goroutine
|
||||
ConnState ConnStateCallback
|
||||
}
|
||||
|
||||
// A Listener listens for incoming QUIC connections
|
||||
type Listener interface {
|
||||
Close() error
|
||||
Addr() net.Addr
|
||||
ListenAddr(addr string) error
|
||||
Listen(conn net.PacketConn) error
|
||||
}
|
||||
|
|
65
server.go
65
server.go
|
@ -2,7 +2,6 @@ package quic
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"strings"
|
||||
|
@ -18,15 +17,14 @@ import (
|
|||
|
||||
// packetHandler handles packets
|
||||
type packetHandler interface {
|
||||
Session
|
||||
handlePacket(*receivedPacket)
|
||||
OpenStream() (utils.Stream, error)
|
||||
run()
|
||||
Close(error) error
|
||||
}
|
||||
|
||||
// A Server of QUIC
|
||||
type Server struct {
|
||||
addr *net.UDPAddr
|
||||
// A Listener of QUIC
|
||||
type server struct {
|
||||
config *Config
|
||||
|
||||
conn net.PacketConn
|
||||
connMutex sync.Mutex
|
||||
|
@ -43,9 +41,11 @@ type Server struct {
|
|||
newSession func(conn connection, v protocol.VersionNumber, connectionID protocol.ConnectionID, sCfg *handshake.ServerConfig, streamCallback StreamCallback, closeCallback closeCallback) (packetHandler, error)
|
||||
}
|
||||
|
||||
// NewServer makes a new server
|
||||
func NewServer(addr string, tlsConfig *tls.Config, cb StreamCallback) (*Server, error) {
|
||||
certChain := crypto.NewCertChain(tlsConfig)
|
||||
var _ Listener = &server{}
|
||||
|
||||
// NewListener makes a new listener
|
||||
func NewListener(config *Config) (Listener, error) {
|
||||
certChain := crypto.NewCertChain(config.TLSConfig)
|
||||
|
||||
kex, err := crypto.NewCurve25519KEX()
|
||||
if err != nil {
|
||||
|
@ -56,33 +56,19 @@ func NewServer(addr string, tlsConfig *tls.Config, cb StreamCallback) (*Server,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Server{
|
||||
addr: udpAddr,
|
||||
return &server{
|
||||
config: config,
|
||||
certChain: certChain,
|
||||
scfg: scfg,
|
||||
streamCallback: cb,
|
||||
streamCallback: func(Session, utils.Stream) {},
|
||||
sessions: map[protocol.ConnectionID]packetHandler{},
|
||||
newSession: newSession,
|
||||
deleteClosedSessionsAfter: protocol.ClosedSessionDeleteTimeout,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ListenAndServe listens and serves a connection
|
||||
func (s *Server) ListenAndServe() error {
|
||||
conn, err := net.ListenUDP("udp", s.addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.Serve(conn)
|
||||
}
|
||||
|
||||
// Serve on an existing PacketConn
|
||||
func (s *Server) Serve(conn net.PacketConn) error {
|
||||
// Listen listens on an existing PacketConn
|
||||
func (s *server) Listen(conn net.PacketConn) error {
|
||||
s.connMutex.Lock()
|
||||
s.conn = conn
|
||||
s.connMutex.Unlock()
|
||||
|
@ -104,8 +90,20 @@ func (s *Server) Serve(conn net.PacketConn) error {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *server) ListenAddr(addr string) error {
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conn, err := net.ListenUDP("udp", udpAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.Listen(conn)
|
||||
}
|
||||
|
||||
// Close the server
|
||||
func (s *Server) Close() error {
|
||||
func (s *server) Close() error {
|
||||
s.sessionsMutex.Lock()
|
||||
for _, session := range s.sessions {
|
||||
if session != nil {
|
||||
|
@ -128,11 +126,11 @@ func (s *Server) Close() error {
|
|||
}
|
||||
|
||||
// Addr returns the server's network address
|
||||
func (s *Server) Addr() net.Addr {
|
||||
return s.addr
|
||||
func (s *server) Addr() net.Addr {
|
||||
return s.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (s *Server) handlePacket(pconn net.PacketConn, remoteAddr net.Addr, packet []byte) error {
|
||||
func (s *server) handlePacket(pconn net.PacketConn, remoteAddr net.Addr, packet []byte) error {
|
||||
if protocol.ByteCount(len(packet)) > protocol.MaxPacketSize {
|
||||
return qerr.PacketTooLarge
|
||||
}
|
||||
|
@ -207,6 +205,7 @@ func (s *Server) handlePacket(pconn net.PacketConn, remoteAddr net.Addr, packet
|
|||
s.sessionsMutex.Lock()
|
||||
s.sessions[hdr.ConnectionID] = session
|
||||
s.sessionsMutex.Unlock()
|
||||
go s.config.ConnState(session, ConnStateVersionNegotiated)
|
||||
}
|
||||
if session == nil {
|
||||
// Late packet for closed session
|
||||
|
@ -221,7 +220,7 @@ func (s *Server) handlePacket(pconn net.PacketConn, remoteAddr net.Addr, packet
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) closeCallback(id protocol.ConnectionID) {
|
||||
func (s *server) closeCallback(id protocol.ConnectionID) {
|
||||
s.sessionsMutex.Lock()
|
||||
s.sessions[id] = nil
|
||||
s.sessionsMutex.Unlock()
|
||||
|
|
171
server_test.go
171
server_test.go
|
@ -9,7 +9,6 @@ import (
|
|||
"github.com/lucas-clemente/quic-go/handshake"
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
"github.com/lucas-clemente/quic-go/qerr"
|
||||
"github.com/lucas-clemente/quic-go/testdata"
|
||||
"github.com/lucas-clemente/quic-go/utils"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
@ -33,10 +32,18 @@ func (s *mockSession) Close(e error) error {
|
|||
s.closed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *mockSession) AcceptStream() (utils.Stream, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
func (s *mockSession) OpenStream() (utils.Stream, error) {
|
||||
return &stream{streamID: 1337}, nil
|
||||
}
|
||||
func (s *mockSession) RemoteAddr() net.Addr {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
var _ Session = &mockSession{}
|
||||
|
||||
func newMockSession(conn connection, v protocol.VersionNumber, connectionID protocol.ConnectionID, sCfg *handshake.ServerConfig, streamCallback StreamCallback, closeCallback closeCallback) (packetHandler, error) {
|
||||
return &mockSession{
|
||||
connectionID: connectionID,
|
||||
|
@ -45,20 +52,28 @@ func newMockSession(conn connection, v protocol.VersionNumber, connectionID prot
|
|||
|
||||
var _ = Describe("Server", func() {
|
||||
var (
|
||||
server *Server
|
||||
conn *mockPacketConn
|
||||
udpAddr *net.UDPAddr
|
||||
firstPacket []byte // a valid first packet for a new connection with connectionID 0x4cfa9f9b668619f6
|
||||
serv *server
|
||||
conn *mockPacketConn
|
||||
connStateStatus ConnState
|
||||
connStateCalled bool
|
||||
firstPacket []byte // a valid first packet for a new connection with connectionID 0x4cfa9f9b668619f6 (= connID)
|
||||
connID = protocol.ConnectionID(0x4cfa9f9b668619f6)
|
||||
udpAddr = &net.UDPAddr{IP: net.IPv4(192, 168, 100, 200), Port: 1337}
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
server = &Server{
|
||||
serv = &server{
|
||||
sessions: map[protocol.ConnectionID]packetHandler{},
|
||||
newSession: newMockSession,
|
||||
conn: &mockPacketConn{},
|
||||
config: &Config{
|
||||
ConnState: func(_ Session, cs ConnState) {
|
||||
connStateStatus = cs
|
||||
connStateCalled = true
|
||||
},
|
||||
},
|
||||
}
|
||||
conn = server.conn.(*mockPacketConn)
|
||||
udpAddr = &net.UDPAddr{IP: net.IPv4(192, 168, 100, 200), Port: 1337}
|
||||
conn = serv.conn.(*mockPacketConn)
|
||||
b := &bytes.Buffer{}
|
||||
utils.WriteUint32(b, protocol.VersionNumberToTag(protocol.SupportedVersions[0]))
|
||||
firstPacket = []byte{0x09, 0xf6, 0x19, 0x86, 0x66, 0x9b, 0x9f, 0xfa, 0x4c}
|
||||
|
@ -67,11 +82,11 @@ var _ = Describe("Server", func() {
|
|||
|
||||
Context("with mock session", func() {
|
||||
It("returns the address", func() {
|
||||
server.addr = &net.UDPAddr{
|
||||
conn.addr = &net.UDPAddr{
|
||||
IP: net.IPv4(192, 168, 13, 37),
|
||||
Port: 1234,
|
||||
}
|
||||
Expect(server.Addr().String()).To(Equal("192.168.13.37:1234"))
|
||||
Expect(serv.Addr().String()).To(Equal("192.168.13.37:1234"))
|
||||
})
|
||||
|
||||
It("composes version negotiation packets", func() {
|
||||
|
@ -83,123 +98,145 @@ var _ = Describe("Server", func() {
|
|||
})
|
||||
|
||||
It("creates new sessions", func() {
|
||||
err := server.handlePacket(nil, nil, firstPacket)
|
||||
err := serv.handlePacket(nil, nil, firstPacket)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(server.sessions).To(HaveLen(1))
|
||||
Expect(server.sessions[0x4cfa9f9b668619f6].(*mockSession).connectionID).To(Equal(protocol.ConnectionID(0x4cfa9f9b668619f6)))
|
||||
Expect(server.sessions[0x4cfa9f9b668619f6].(*mockSession).packetCount).To(Equal(1))
|
||||
Expect(serv.sessions).To(HaveLen(1))
|
||||
Expect(serv.sessions[connID].(*mockSession).connectionID).To(Equal(connID))
|
||||
Expect(serv.sessions[connID].(*mockSession).packetCount).To(Equal(1))
|
||||
Eventually(func() bool { return connStateCalled }).Should(BeTrue())
|
||||
Expect(connStateStatus).To(Equal(ConnStateVersionNegotiated))
|
||||
})
|
||||
|
||||
It("assigns packets to existing sessions", func() {
|
||||
err := server.handlePacket(nil, nil, firstPacket)
|
||||
err := serv.handlePacket(nil, nil, firstPacket)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = server.handlePacket(nil, nil, []byte{0x08, 0xf6, 0x19, 0x86, 0x66, 0x9b, 0x9f, 0xfa, 0x4c, 0x01})
|
||||
err = serv.handlePacket(nil, nil, []byte{0x08, 0xf6, 0x19, 0x86, 0x66, 0x9b, 0x9f, 0xfa, 0x4c, 0x01})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(server.sessions).To(HaveLen(1))
|
||||
Expect(server.sessions[0x4cfa9f9b668619f6].(*mockSession).connectionID).To(Equal(protocol.ConnectionID(0x4cfa9f9b668619f6)))
|
||||
Expect(server.sessions[0x4cfa9f9b668619f6].(*mockSession).packetCount).To(Equal(2))
|
||||
Expect(serv.sessions).To(HaveLen(1))
|
||||
Expect(serv.sessions[connID].(*mockSession).connectionID).To(Equal(connID))
|
||||
Expect(serv.sessions[connID].(*mockSession).packetCount).To(Equal(2))
|
||||
})
|
||||
|
||||
It("closes and deletes sessions", func() {
|
||||
server.deleteClosedSessionsAfter = time.Second // make sure that the nil value for the closed session doesn't get deleted in this test
|
||||
err := server.handlePacket(nil, nil, append(firstPacket, (&crypto.NullAEAD{}).Seal(nil, nil, 0, firstPacket)...))
|
||||
serv.deleteClosedSessionsAfter = time.Second // make sure that the nil value for the closed session doesn't get deleted in this test
|
||||
err := serv.handlePacket(nil, nil, append(firstPacket, (&crypto.NullAEAD{}).Seal(nil, nil, 0, firstPacket)...))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(server.sessions).To(HaveLen(1))
|
||||
Expect(server.sessions[0x4cfa9f9b668619f6]).ToNot(BeNil())
|
||||
server.closeCallback(0x4cfa9f9b668619f6)
|
||||
Expect(serv.sessions).To(HaveLen(1))
|
||||
Expect(serv.sessions[connID]).ToNot(BeNil())
|
||||
serv.closeCallback(connID)
|
||||
// The server should now have closed the session, leaving a nil value in the sessions map
|
||||
Expect(server.sessions).To(HaveLen(1))
|
||||
Expect(server.sessions[0x4cfa9f9b668619f6]).To(BeNil())
|
||||
Expect(serv.sessions).To(HaveLen(1))
|
||||
Expect(serv.sessions[connID]).To(BeNil())
|
||||
})
|
||||
|
||||
It("deletes nil session entries after a wait time", func() {
|
||||
server.deleteClosedSessionsAfter = 25 * time.Millisecond
|
||||
err := server.handlePacket(nil, nil, append(firstPacket, (&crypto.NullAEAD{}).Seal(nil, nil, 0, firstPacket)...))
|
||||
serv.deleteClosedSessionsAfter = 25 * time.Millisecond
|
||||
err := serv.handlePacket(nil, nil, append(firstPacket, (&crypto.NullAEAD{}).Seal(nil, nil, 0, firstPacket)...))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(server.sessions).To(HaveLen(1))
|
||||
server.closeCallback(0x4cfa9f9b668619f6)
|
||||
Expect(server.sessions).To(HaveKey(protocol.ConnectionID(0x4cfa9f9b668619f6)))
|
||||
Eventually(func() map[protocol.ConnectionID]packetHandler { return server.sessions }).ShouldNot(HaveKey(protocol.ConnectionID(0x4cfa9f9b668619f6)))
|
||||
Expect(serv.sessions).To(HaveLen(1))
|
||||
serv.closeCallback(connID)
|
||||
Expect(serv.sessions).To(HaveKey(connID))
|
||||
Eventually(func() map[protocol.ConnectionID]packetHandler { return serv.sessions }).ShouldNot(HaveKey(connID))
|
||||
})
|
||||
|
||||
It("closes sessions and the connection when Close is called", func() {
|
||||
session := &mockSession{}
|
||||
server.sessions[1] = session
|
||||
err := server.Close()
|
||||
serv.sessions[1] = session
|
||||
err := serv.Close()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(session.closed).To(BeTrue())
|
||||
Expect(conn.closed).To(BeTrue())
|
||||
})
|
||||
|
||||
It("ignores packets for closed sessions", func() {
|
||||
server.sessions[0x4cfa9f9b668619f6] = nil
|
||||
err := server.handlePacket(nil, nil, []byte{0x08, 0xf6, 0x19, 0x86, 0x66, 0x9b, 0x9f, 0xfa, 0x4c, 0x01})
|
||||
serv.sessions[connID] = nil
|
||||
err := serv.handlePacket(nil, nil, []byte{0x08, 0xf6, 0x19, 0x86, 0x66, 0x9b, 0x9f, 0xfa, 0x4c, 0x01})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(server.sessions).To(HaveLen(1))
|
||||
Expect(server.sessions[0x4cfa9f9b668619f6]).To(BeNil())
|
||||
Expect(serv.sessions).To(HaveLen(1))
|
||||
Expect(serv.sessions[connID]).To(BeNil())
|
||||
})
|
||||
|
||||
It("ignores delayed packets with mismatching versions", func() {
|
||||
err := server.handlePacket(nil, nil, firstPacket)
|
||||
err := serv.handlePacket(nil, nil, firstPacket)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(server.sessions[0x4cfa9f9b668619f6].(*mockSession).packetCount).To(Equal(1))
|
||||
Expect(serv.sessions[connID].(*mockSession).packetCount).To(Equal(1))
|
||||
b := &bytes.Buffer{}
|
||||
// add an unsupported version
|
||||
utils.WriteUint32(b, protocol.VersionNumberToTag(protocol.SupportedVersions[0]-2))
|
||||
data := []byte{0x09, 0xf6, 0x19, 0x86, 0x66, 0x9b, 0x9f, 0xfa, 0x4c}
|
||||
data = append(append(data, b.Bytes()...), 0x01)
|
||||
err = server.handlePacket(nil, nil, data)
|
||||
err = serv.handlePacket(nil, nil, data)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// if we didn't ignore the packet, the server would try to send a version negotation packet, which would make the test panic because it doesn't have a udpConn
|
||||
Expect(conn.dataWritten.Bytes()).To(BeEmpty())
|
||||
// make sure the packet was *not* passed to session.handlePacket()
|
||||
Expect(server.sessions[0x4cfa9f9b668619f6].(*mockSession).packetCount).To(Equal(1))
|
||||
Expect(serv.sessions[connID].(*mockSession).packetCount).To(Equal(1))
|
||||
})
|
||||
|
||||
It("errors on invalid public header", func() {
|
||||
err := server.handlePacket(nil, nil, nil)
|
||||
err := serv.handlePacket(nil, nil, nil)
|
||||
Expect(err.(*qerr.QuicError).ErrorCode).To(Equal(qerr.InvalidPacketHeader))
|
||||
})
|
||||
|
||||
It("errors on large packets", func() {
|
||||
err := server.handlePacket(nil, nil, bytes.Repeat([]byte{'a'}, int(protocol.MaxPacketSize)+1))
|
||||
err := serv.handlePacket(nil, nil, bytes.Repeat([]byte{'a'}, int(protocol.MaxPacketSize)+1))
|
||||
Expect(err).To(MatchError(qerr.PacketTooLarge))
|
||||
})
|
||||
|
||||
It("ignores public resets for unknown connections", func() {
|
||||
err := server.handlePacket(nil, nil, writePublicReset(999, 1, 1337))
|
||||
err := serv.handlePacket(nil, nil, writePublicReset(999, 1, 1337))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(server.sessions).To(BeEmpty())
|
||||
Expect(serv.sessions).To(BeEmpty())
|
||||
})
|
||||
|
||||
It("ignores public resets for known connections", func() {
|
||||
err := server.handlePacket(nil, nil, firstPacket)
|
||||
Expect(server.sessions).To(HaveLen(1))
|
||||
Expect(server.sessions[0x4cfa9f9b668619f6].(*mockSession).packetCount).To(Equal(1))
|
||||
err = server.handlePacket(nil, nil, writePublicReset(0x4cfa9f9b668619f6, 1, 1337))
|
||||
err := serv.handlePacket(nil, nil, firstPacket)
|
||||
Expect(serv.sessions).To(HaveLen(1))
|
||||
Expect(serv.sessions[connID].(*mockSession).packetCount).To(Equal(1))
|
||||
err = serv.handlePacket(nil, nil, writePublicReset(connID, 1, 1337))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(server.sessions).To(HaveLen(1))
|
||||
Expect(server.sessions[0x4cfa9f9b668619f6].(*mockSession).packetCount).To(Equal(1))
|
||||
Expect(serv.sessions).To(HaveLen(1))
|
||||
Expect(serv.sessions[connID].(*mockSession).packetCount).To(Equal(1))
|
||||
})
|
||||
|
||||
It("ignores invalid public resets for known connections", func() {
|
||||
err := server.handlePacket(nil, nil, firstPacket)
|
||||
Expect(server.sessions).To(HaveLen(1))
|
||||
Expect(server.sessions[0x4cfa9f9b668619f6].(*mockSession).packetCount).To(Equal(1))
|
||||
data := writePublicReset(0x4cfa9f9b668619f6, 1, 1337)
|
||||
err = server.handlePacket(nil, nil, data[:len(data)-2])
|
||||
err := serv.handlePacket(nil, nil, firstPacket)
|
||||
Expect(serv.sessions).To(HaveLen(1))
|
||||
Expect(serv.sessions[connID].(*mockSession).packetCount).To(Equal(1))
|
||||
data := writePublicReset(connID, 1, 1337)
|
||||
err = serv.handlePacket(nil, nil, data[:len(data)-2])
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(server.sessions).To(HaveLen(1))
|
||||
Expect(server.sessions[0x4cfa9f9b668619f6].(*mockSession).packetCount).To(Equal(1))
|
||||
Expect(serv.sessions).To(HaveLen(1))
|
||||
Expect(serv.sessions[connID].(*mockSession).packetCount).To(Equal(1))
|
||||
})
|
||||
})
|
||||
|
||||
It("setups with the right values", func() {
|
||||
s, err := NewServer("", testdata.GetTLSConfig(), nil)
|
||||
config := Config{
|
||||
ConnState: func(_ Session, _ ConnState) {},
|
||||
}
|
||||
ln, err := NewListener(&config)
|
||||
server := ln.(*server)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(s.deleteClosedSessionsAfter).To(Equal(protocol.ClosedSessionDeleteTimeout))
|
||||
Expect(s.sessions).ToNot(BeNil())
|
||||
Expect(s.scfg).ToNot(BeNil())
|
||||
Expect(server.deleteClosedSessionsAfter).To(Equal(protocol.ClosedSessionDeleteTimeout))
|
||||
Expect(server.sessions).ToNot(BeNil())
|
||||
Expect(server.scfg).ToNot(BeNil())
|
||||
Expect(server.config).To(Equal(&config))
|
||||
})
|
||||
|
||||
It("listens on a given address", func() {
|
||||
var listenReturned bool
|
||||
addr := "127.0.0.1:13579"
|
||||
serv.conn = nil
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
err := serv.ListenAddr(addr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
listenReturned = true
|
||||
}()
|
||||
Eventually(func() net.PacketConn { return serv.conn }).ShouldNot(BeNil())
|
||||
Expect(serv.Addr().String()).To(Equal(addr))
|
||||
Consistently(func() bool { return listenReturned }).Should(BeFalse())
|
||||
})
|
||||
|
||||
It("setups and responds with version negotiation", func() {
|
||||
|
@ -207,7 +244,7 @@ var _ = Describe("Server", func() {
|
|||
conn.dataReadFrom = udpAddr
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
err := server.Serve(conn)
|
||||
err := serv.Listen(conn)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}()
|
||||
|
||||
|
@ -225,13 +262,13 @@ var _ = Describe("Server", func() {
|
|||
conn.dataToRead = []byte{0x08, 0xf6, 0x19, 0x86, 0x66, 0x9b, 0x9f, 0xfa, 0x4c, 0x01}
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
err := server.Serve(conn)
|
||||
err := serv.Listen(conn)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}()
|
||||
|
||||
Eventually(func() int { return conn.dataWritten.Len() }).ShouldNot(BeZero())
|
||||
Expect(conn.dataWrittenTo).To(Equal(udpAddr))
|
||||
Expect(conn.dataWritten.Bytes()[0] & 0x02).ToNot(BeZero()) // check that the ResetFlag is set
|
||||
Expect(server.sessions).To(BeEmpty())
|
||||
Expect(serv.sessions).To(BeEmpty())
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue