aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <m.shulhan@gmail.com>2026-02-16 15:22:59 +0700
committerShulhan <m.shulhan@gmail.com>2026-04-15 01:16:22 +0700
commiteb17f74b599d53a21952ee0b96ab68ed3cb86e3d (patch)
treebe3c362bcbe9d4d7bc0676f17e85ed3d86692d77
parente64c25877e784baab341663fbc75ff909ee404f5 (diff)
downloadgo-x-pkgsite-main.tar.xz
[DO-NOT-MERGE] all: add option shutdown-idleHEADmain
The shutdown-idle set the duration to automatically shutdown the HTTP server when no request after specific duration. This is to complement the socket based activation to minimize the resources on local environment.
-rw-r--r--cmd/pkgsite/main.go33
-rw-r--r--internal/middleware/timeout/timeout.go10
2 files changed, 42 insertions, 1 deletions
diff --git a/cmd/pkgsite/main.go b/cmd/pkgsite/main.go
index eae690e0..06968618 100644
--- a/cmd/pkgsite/main.go
+++ b/cmd/pkgsite/main.go
@@ -89,6 +89,10 @@ func main() {
flag.BoolVar(&serverCfg.DevMode, "dev", false, "enable developer mode (reload templates on each page load, serve non-minified JS/CSS, etc.)")
flag.StringVar(&serverCfg.DevModeStaticDir, "static", "static", "path to folder containing static files served")
+ var shutdownIdleDuration string
+ flag.StringVar(&shutdownIdleDuration, `shutdown-idle`, ``,
+ `Shutdown the server after idle on specific duration.`)
+
flag.Usage = func() {
out := flag.CommandLine.Output()
fmt.Fprintf(out, "usage: %s [flags] [PATHS ...]\n", os.Args[0])
@@ -98,6 +102,15 @@ func main() {
}
flag.Parse()
+ if shutdownIdleDuration != `` {
+ var err error
+ timeout.ShutdownIdleDuration, err = time.ParseDuration(shutdownIdleDuration)
+ if err != nil {
+ dief(`invalid shutdown-idle duration %s`, shutdownIdleDuration)
+ }
+ timeout.ShutdownIdleTimer = time.NewTimer(timeout.ShutdownIdleDuration)
+ }
+
serverCfg.UseLocalStdlib = true
serverCfg.GoRepoPath = *goRepoPath
serverCfg.Paths = collectPaths(flag.Args())
@@ -171,7 +184,25 @@ func main() {
server.Install(router.Handle, nil, nil)
mw := timeout.Timeout(54 * time.Second)
srv := &http.Server{Addr: addr, Handler: mw(router)}
- dief("%v", srv.Serve(ln))
+
+ if timeout.ShutdownIdleTimer == nil {
+ dief("%v", srv.Serve(ln))
+ } else {
+ waitq := make(chan struct{}, 1)
+ go func() {
+ err = srv.Serve(ln)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s\n", err.Error())
+ }
+ waitq <- struct{}{}
+ }()
+ select {
+ case <-waitq:
+ // Server stopped.
+ case <-timeout.ShutdownIdleTimer.C:
+ _ = srv.Shutdown(context.Background())
+ }
+ }
}
func dief(format string, args ...any) {
diff --git a/internal/middleware/timeout/timeout.go b/internal/middleware/timeout/timeout.go
index 93d992da..391919c4 100644
--- a/internal/middleware/timeout/timeout.go
+++ b/internal/middleware/timeout/timeout.go
@@ -10,11 +10,21 @@ import (
"time"
)
+var ShutdownIdleDuration time.Duration
+var ShutdownIdleTimer *time.Timer
+
// Timeout returns a new Middleware that times out each request after the given
// duration.
func Timeout(d time.Duration) func(http.Handler) http.Handler {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if ShutdownIdleTimer != nil {
+ ok := ShutdownIdleTimer.Reset(ShutdownIdleDuration)
+ if !ok {
+ // Timer had reached or closed
+ return
+ }
+ }
ctx, cancel := context.WithTimeout(r.Context(), d)
defer cancel()
h.ServeHTTP(w, r.WithContext(ctx))