gin/graceful.go
Rikiya Narita f894f4c7ed feat(engine): add Shutdown method for graceful shutdown support
- Add server and serverLock fields to Engine struct
- Add Shutdown() method that calls http.Server.Shutdown()
- Modify Run/RunTLS/RunUnix/RunListener to store server reference
- Add RunWithShutdown convenience method with signal handling
- Add comprehensive tests for graceful shutdown (8 test cases)
- Fix lint errors (errorlint, testifylint, errcheck)
2026-01-19 11:29:52 +09:00

70 lines
1.9 KiB
Go

// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package gin
import (
"context"
"errors"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
// ShutdownConfig holds configuration for graceful shutdown.
type ShutdownConfig struct {
// Timeout is the maximum duration to wait for active connections to finish.
// Default: 10 seconds
Timeout time.Duration
// Signals are the OS signals that will trigger shutdown.
// Default: SIGINT, SIGTERM
Signals []os.Signal
}
// RunWithShutdown starts the HTTP server and handles graceful shutdown on SIGINT/SIGTERM.
// It blocks until the server is shut down.
// The timeout parameter specifies the maximum duration to wait for active connections to finish.
func (engine *Engine) RunWithShutdown(addr string, timeout time.Duration) error {
return engine.RunWithShutdownConfig(addr, ShutdownConfig{
Timeout: timeout,
Signals: []os.Signal{syscall.SIGINT, syscall.SIGTERM},
})
}
// RunWithShutdownConfig starts the HTTP server with custom shutdown configuration.
// It blocks until the server is shut down.
func (engine *Engine) RunWithShutdownConfig(addr string, config ShutdownConfig) error {
if config.Timeout == 0 {
config.Timeout = 10 * time.Second
}
if len(config.Signals) == 0 {
config.Signals = []os.Signal{syscall.SIGINT, syscall.SIGTERM}
}
ctx, stop := signal.NotifyContext(context.Background(), config.Signals...)
defer stop()
errCh := make(chan error, 1)
go func() {
if err := engine.Run(addr); err != nil && !errors.Is(err, http.ErrServerClosed) {
errCh <- err
}
close(errCh)
}()
select {
case err := <-errCh:
return err
case <-ctx.Done():
}
shutdownCtx, cancel := context.WithTimeout(context.Background(), config.Timeout)
defer cancel()
return engine.Shutdown(shutdownCtx)
}