implement serving multiple connections at the same time

This commit is contained in:
Marten Seemann
2019-09-05 13:10:26 +07:00
parent d1489e5045
commit 717e6d5c96
4 changed files with 211 additions and 65 deletions

View File

@@ -39,9 +39,9 @@ type Server struct {
port uint32 // used atomically
listenerMutex sync.Mutex
listener quic.Listener
closed bool
mutex sync.Mutex
listeners map[*quic.Listener]struct{}
closed utils.AtomicBool
supportedVersionsAsString string
@@ -80,19 +80,13 @@ func (s *Server) Serve(conn net.PacketConn) error {
}
func (s *Server) serveImpl(tlsConf *tls.Config, conn net.PacketConn) error {
if s.closed.Get() {
return http.ErrServerClosed
}
if s.Server == nil {
return errors.New("use of http3.Server without http.Server")
}
s.logger = utils.DefaultLogger.WithPrefix("server")
s.listenerMutex.Lock()
if s.closed {
s.listenerMutex.Unlock()
return errors.New("Server is already closed")
}
if s.listener != nil {
s.listenerMutex.Unlock()
return errors.New("ListenAndServe may only be called once")
}
if tlsConf == nil {
tlsConf = &tls.Config{}
@@ -121,11 +115,10 @@ func (s *Server) serveImpl(tlsConf *tls.Config, conn net.PacketConn) error {
ln, err = quicListen(conn, tlsConf, s.QuicConfig)
}
if err != nil {
s.listenerMutex.Unlock()
return err
}
s.listener = ln
s.listenerMutex.Unlock()
s.addListener(&ln)
defer s.removeListener(&ln)
for {
sess, err := ln.Accept(context.Background())
@@ -136,6 +129,24 @@ func (s *Server) serveImpl(tlsConf *tls.Config, conn net.PacketConn) error {
}
}
// We store a pointer to interface in the map set. This is safe because we only
// call trackListener via Serve and can track+defer untrack the same pointer to
// local variable there. We never need to compare a Listener from another caller.
func (s *Server) addListener(l *quic.Listener) {
s.mutex.Lock()
if s.listeners == nil {
s.listeners = make(map[*quic.Listener]struct{})
}
s.listeners[l] = struct{}{}
s.mutex.Unlock()
}
func (s *Server) removeListener(l *quic.Listener) {
s.mutex.Lock()
delete(s.listeners, l)
s.mutex.Unlock()
}
func (s *Server) handleConn(sess quic.Session) {
// TODO: accept control streams
decoder := qpack.NewDecoder(nil)
@@ -256,15 +267,18 @@ func (s *Server) handleRequest(str quic.Stream, decoder *qpack.Decoder) error {
// 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.listenerMutex.Lock()
defer s.listenerMutex.Unlock()
s.closed = true
if s.listener != nil {
err := s.listener.Close()
s.listener = nil
return err
s.closed.Set(true)
s.mutex.Lock()
defer s.mutex.Unlock()
var err error
for ln := range s.listeners {
if cerr := (*ln).Close(); cerr != nil && err == nil {
err = cerr
}
}
return nil
return err
}
// CloseGracefully shuts down the server gracefully. The server sends a GOAWAY frame first, then waits for either timeout to trigger, or for all running requests to complete.