79 lines
1.8 KiB
Go
79 lines
1.8 KiB
Go
package mux
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"log"
|
|
"log/slog"
|
|
"net"
|
|
"net/http"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
)
|
|
|
|
type ServeCB func(srv *http.Server) error
|
|
|
|
const (
|
|
shutdownDelay = time.Second * 10
|
|
shutdownHardDelay = time.Second * 5
|
|
drainDelay = time.Second
|
|
)
|
|
|
|
// Serve with graceful shutdown
|
|
func (m *Mux) Serve(cb ServeCB) {
|
|
rootCtx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
|
defer stop()
|
|
|
|
// catch all options
|
|
// lets get it thorugh all middlewares
|
|
m.OPTIONS("/", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Length", "0")
|
|
if r.ContentLength != 0 {
|
|
// Read up to 4KB of OPTIONS body (as mentioned in the
|
|
// spec as being reserved for future use), but anything
|
|
// over that is considered a waste of server resources
|
|
// (or an attack) and we abort and close the connection,
|
|
// courtesy of MaxBytesReader's EOF behavior.
|
|
mb := http.MaxBytesReader(w, r.Body, 4<<10)
|
|
io.Copy(io.Discard, mb)
|
|
}
|
|
})
|
|
|
|
srvCtx, cancelSrvCtx := context.WithCancel(context.Background())
|
|
srv := &http.Server{
|
|
Handler: m,
|
|
BaseContext: func(_ net.Listener) context.Context {
|
|
return srvCtx
|
|
},
|
|
}
|
|
|
|
go func() {
|
|
if err := cb(srv); !errors.Is(err, http.ErrServerClosed) {
|
|
panic(err)
|
|
}
|
|
}()
|
|
|
|
// Wait for interrupt signal
|
|
<-rootCtx.Done()
|
|
|
|
stop()
|
|
m.IsShuttingDown.Store(true)
|
|
slog.Info("received interrupt singal, shutting down")
|
|
time.Sleep(drainDelay)
|
|
slog.Info("readiness check propagated, now waiting for ongoing requests to finish.")
|
|
|
|
shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownDelay)
|
|
defer cancel()
|
|
|
|
err := srv.Shutdown(shutdownCtx)
|
|
cancelSrvCtx()
|
|
if err != nil {
|
|
log.Println("failed to wait for ongoing requests to finish, waiting for forced cancellation")
|
|
time.Sleep(shutdownHardDelay)
|
|
}
|
|
|
|
slog.Info("seerver shut down gracefully")
|
|
}
|