From 41e00da4627a9abac9bd7b9e1788691c53314cd5 Mon Sep 17 00:00:00 2001 From: rocketlaunchr-cto Date: Tue, 22 Nov 2022 09:20:55 +1100 Subject: [PATCH 1/4] add ShutdownWithTimeout function --- app.go | 28 +++++++++++++++++++++++----- go.mod | 2 +- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/app.go b/app.go index f725424ee6..583e272cd2 100644 --- a/app.go +++ b/app.go @@ -9,6 +9,9 @@ package fiber import ( "bufio" + "context" + "encoding/json" + "encoding/xml" "errors" "fmt" "net" @@ -20,9 +23,6 @@ import ( "sync" "time" - "encoding/json" - "encoding/xml" - "github.com/gofiber/fiber/v2/utils" "github.com/valyala/fasthttp" ) @@ -802,12 +802,30 @@ func (app *App) HandlersCount() uint32 { } // Shutdown gracefully shuts down the server without interrupting any active connections. -// Shutdown works by first closing all open listeners and then waiting indefinitely for all connections to return to idle and then shut down. +// Shutdown works by first closing all open listeners and then waiting indefinitely for all connections to return to idle before shutting down. // // Make sure the program doesn't exit and waits instead for Shutdown to return. // // 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()) +} + +// ShutdownWithTimeout gracefully shuts down the server without interrupting any active connections. However, if the timeout is exceeded, +// ShutdownWithTimeout will forcefully close any active connections. +// ShutdownWithTimeout works by first closing all open listeners and then waiting for all connections to return to idle before shutting down. +// +// Make sure the program doesn't exit and waits instead for ShutdownWithTimeout to return. +// +// ShutdownWithTimeout does not close keepalive connections so its recommended to set ReadTimeout to something else than 0. +func (app *App) ShutdownWithTimeout(timeout time.Duration) error { + ctx, cancelFunc := context.WithTimeout(context.Background(), timeout) + defer cancelFunc() + return app.shutdownWithContext(ctx) +} + +// shutdownWithContext shutsdown the server including by force if the context's deadline is exceeded. +func (app *App) shutdownWithContext(ctx context.Context) error { if app.hooks != nil { defer app.hooks.executeOnShutdownHooks() } @@ -817,7 +835,7 @@ func (app *App) Shutdown() error { if app.server == nil { return fmt.Errorf("shutdown: server is not running") } - return app.server.Shutdown() + return app.server.ShutdownWithContext(ctx) } // Server returns the underlying fasthttp server diff --git a/go.mod b/go.mod index d9239e4919..aa55b756e4 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.16 github.com/mattn/go-runewidth v0.0.14 - github.com/valyala/fasthttp v1.41.0 + github.com/valyala/fasthttp v1.42.0 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab ) From d68840430cf6de526af15beb7c9a0b0f06b21cb8 Mon Sep 17 00:00:00 2001 From: rocketlaunchr-cto Date: Mon, 28 Nov 2022 17:46:50 +1100 Subject: [PATCH 2/4] no message --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f886dc061f..634a4674d6 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,8 @@ require ( github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.16 github.com/mattn/go-runewidth v0.0.14 - github.com/valyala/fasthttp v1.42.0 github.com/valyala/bytebufferpool v1.0.0 + github.com/valyala/fasthttp v1.42.0 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab ) diff --git a/go.sum b/go.sum index b84535bd1f..546eef5258 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,8 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.41.0 h1:zeR0Z1my1wDHTRiamBCXVglQdbUwgb9uWG3k1HQz6jY= -github.com/valyala/fasthttp v1.41.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY= +github.com/valyala/fasthttp v1.42.0 h1:LBMyqvJR8DEBgN79oI8dGbkuj5Lm9jbHESxH131TTN8= +github.com/valyala/fasthttp v1.42.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= From 1976966d84385656752ff9c259fbad2e6e5cf470 Mon Sep 17 00:00:00 2001 From: rocketlaunchr-cto Date: Mon, 28 Nov 2022 18:25:14 +1100 Subject: [PATCH 3/4] fix func documentation --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index ca16d11f2b..717db86f38 100644 --- a/app.go +++ b/app.go @@ -862,7 +862,7 @@ func (app *App) ShutdownWithTimeout(timeout time.Duration) error { return app.shutdownWithContext(ctx) } -// shutdownWithContext shutsdown the server including by force if the context's deadline is exceeded. +// shutdownWithContext shuts down the server including by force if the context's deadline is exceeded. func (app *App) shutdownWithContext(ctx context.Context) error { if app.hooks != nil { defer app.hooks.executeOnShutdownHooks() From 55c5a87d3db0e89d626bfa3d285b8be3fc8fdbad Mon Sep 17 00:00:00 2001 From: kinggo Date: Wed, 14 Dec 2022 15:55:21 +0800 Subject: [PATCH 4/4] test: add Test_App_ShutdownWithTimeout --- app_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/app_test.go b/app_test.go index 7d532f3143..343e012783 100644 --- a/app_test.go +++ b/app_test.go @@ -6,6 +6,7 @@ package fiber import ( "bytes" + "context" "crypto/tls" "errors" "fmt" @@ -23,6 +24,7 @@ import ( "github.com/gofiber/fiber/v2/utils" "github.com/valyala/fasthttp" + "github.com/valyala/fasthttp/fasthttputil" ) var testEmptyHandler = func(c *Ctx) error { @@ -707,6 +709,45 @@ func Test_App_Shutdown(t *testing.T) { }) } +func Test_App_ShutdownWithTimeout(t *testing.T) { + 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() { + shutdownErr <- app.ShutdownWithTimeout(1 * time.Second) + }() + + timer := time.NewTimer(time.Second * 5) + select { + case <-timer.C: + t.Fatal("idle connections not closed on shutdown") + case err := <-shutdownErr: + if err == nil || 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) { app := New()