🚀 Feature: Public ShutdownWithContext (#2407)

* feat: public shutdown with context

* docs: add server shutdown option

* chore: revert spacing changes

* test: app shutdown with context
pull/2408/head^2
Juan C. Yamacho H 2023-04-09 15:08:03 +02:00 committed by GitHub
parent 1c5eb1846e
commit 562d15db86
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 58 additions and 4 deletions

12
app.go
View File

@ -847,7 +847,7 @@ func (app *App) HandlersCount() uint32 {
//
// Shutdown does not close keepalive connections so its recommended to set ReadTimeout to something else than 0.
func (app *App) Shutdown() error {
return app.shutdownWithContext(context.Background())
return app.ShutdownWithContext(context.Background())
}
// ShutdownWithTimeout gracefully shuts down the server without interrupting any active connections. However, if the timeout is exceeded,
@ -860,11 +860,15 @@ func (app *App) Shutdown() error {
func (app *App) ShutdownWithTimeout(timeout time.Duration) error {
ctx, cancelFunc := context.WithTimeout(context.Background(), timeout)
defer cancelFunc()
return app.shutdownWithContext(ctx)
return app.ShutdownWithContext(ctx)
}
// shutdownWithContext shuts down the server including by force if the context's deadline is exceeded.
func (app *App) shutdownWithContext(ctx context.Context) error {
// ShutdownWithContext shuts down the server including by force if the context's deadline is exceeded.
//
// Make sure the program doesn't exit and waits instead for ShutdownWithTimeout to return.
//
// ShutdownWithContext does not close keepalive connections so its recommended to set ReadTimeout to something else than 0.
func (app *App) ShutdownWithContext(ctx context.Context) error {
if app.hooks != nil {
defer app.hooks.executeOnShutdownHooks()
}

View File

@ -788,6 +788,53 @@ func Test_App_ShutdownWithTimeout(t *testing.T) {
}
}
func Test_App_ShutdownWithContext(t *testing.T) {
t.Parallel()
app := New()
app.Get("/", func(ctx *Ctx) error {
time.Sleep(5 * time.Second)
return ctx.SendString("body")
})
ln := fasthttputil.NewInmemoryListener()
go func() {
utils.AssertEqual(t, nil, app.Listener(ln))
}()
time.Sleep(1 * time.Second)
go func() {
conn, err := ln.Dial()
if err != nil {
t.Errorf("unexepcted error: %v", err)
}
if _, err = conn.Write([]byte("GET / HTTP/1.1\r\nHost: google.com\r\n\r\n")); err != nil {
t.Errorf("unexpected error: %v", err)
}
}()
time.Sleep(1 * time.Second)
shutdownErr := make(chan error)
go func() {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
shutdownErr <- app.ShutdownWithContext(ctx)
}()
select {
case <-time.After(5 * time.Second):
t.Fatal("idle connections not closed on shutdown")
case err := <-shutdownErr:
if err == nil || !errors.Is(err, context.DeadlineExceeded) {
t.Fatalf("unexpected err %v. Expecting %v", err, context.DeadlineExceeded)
}
}
}
// go test -run Test_App_Static_Index_Default
func Test_App_Static_Index_Default(t *testing.T) {
t.Parallel()

View File

@ -236,9 +236,12 @@ Shutdown gracefully shuts down the server without interrupting any active connec
ShutdownWithTimeout will forcefully close any active connections after the timeout expires.
ShutdownWithContext shuts down the server including by force if the context's deadline is exceeded.
```go
func (app *App) Shutdown() error
func (app *App) ShutdownWithTimeout(timeout time.Duration) error
func (app *App) ShutdownWithContext(ctx context.Context) error
```
## HandlersCount