Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 11, 22:15 UTC
caddyhttp: Free up quic listener when stopping (#7177)
WeidiDeng committed
Aug 14, 2025 at 02:35 UTC
7590c9ca1ba6096408574a7dd3b9dc42d12cb948
3 files changed
+24
-1
listeners.go
+2
@@ -430,6 +430,7 @@ func JoinNetworkAddress(network, host, port string) string {
430
// address instead.
431
//
432
// NOTE: This API is EXPERIMENTAL and may be changed or removed.
433
+// NOTE: user should close the returned listener twice, once to stop accepting new connections, the second time to free up the packet conn.
434
func (na NetworkAddress) ListenQUIC(ctx context.Context, portOffset uint, config net.ListenConfig, tlsConf *tls.Config) (http3.QUICListener, error) {
435
lnKey := listenerKey("quic"+na.Network, na.JoinHostPort(portOffset))
436
@@ -626,6 +627,7 @@ func (fcql *fakeCloseQuicListener) Accept(_ context.Context) (*quic.Conn, error)
627
func (fcql *fakeCloseQuicListener) Close() error {
628
if atomic.CompareAndSwapInt32(&fcql.closed, 0, 1) {
629
fcql.contextCancel()
630
+ } else if atomic.CompareAndSwapInt32(&fcql.closed, 1, 2) {
631
_, _ = listenerPool.Delete(fcql.sharedQuicListener.key)
632
}
633
return nil
modules/caddyhttp/app.go
+18
@@ -722,11 +722,29 @@ func (app *App) Stop() error {
722
return
723
}
724
725
+ // closing quic listeners won't affect accepted connections now
726
+ // so like stdlib, close listeners first, but keep the net.PacketConns open
727
+ for _, h3ln := range server.quicListeners {
728
+ if err := h3ln.Close(); err != nil {
729
+ app.logger.Error("http3 listener close",
730
+ zap.Error(err))
731
+ }
732
+ }
733
+
734
if err := server.h3server.Shutdown(ctx); err != nil {
735
app.logger.Error("HTTP/3 server shutdown",
736
zap.Error(err),
737
zap.Strings("addresses", server.Listen))
738
}
739
+
740
+ // close the underlying net.PacketConns now
741
+ // see the comment for ListenQUIC
742
+ for _, h3ln := range server.quicListeners {
743
+ if err := h3ln.Close(); err != nil {
744
+ app.logger.Error("http3 listener close socket",
745
+ zap.Error(err))
746
+ }
747
+ }
748
}
749
stopH2Listener := func(server *Server) {
750
defer finishedShutdown.Done()
modules/caddyhttp/server.go
+4
-1
@@ -235,7 +235,8 @@ type Server struct {
235
primaryHandlerChain Handler
236
errorHandlerChain Handler
237
listenerWrappers []caddy.ListenerWrapper
238
- listeners []net.Listener
238
+ listeners []net.Listener // stdlib http.Server will close these
239
+ quicListeners []http3.QUICListener // http3 now leave the quic.Listener management to us
240
241
tlsApp *caddytls.TLS
242
events *caddyevents.App
@@ -626,6 +627,8 @@ func (s *Server) serveHTTP3(addr caddy.NetworkAddress, tlsCfg *tls.Config) error
627
}
628
}
629
630
+ s.quicListeners = append(s.quicListeners, h3ln)
631
+
632
//nolint:errcheck
633
go s.h3server.ServeListener(h3ln)
634