diff --git a/database.go b/database.go index 8222d12..b485474 100644 --- a/database.go +++ b/database.go @@ -36,6 +36,10 @@ func initDatabase() (err error) { if err != nil { return err } + addShutdownFunc(func() { + _ = closeDb() + log.Println("Closed database") + }) vacuumDb() err = migrateDb() if err != nil { diff --git a/go.mod b/go.mod index 1a5ff28..9a6d7b8 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/andybalholm/cascadia v1.2.0 // indirect github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/boombuler/barcode v1.0.1 // indirect - github.com/caddyserver/certmagic v0.13.0 + github.com/caddyserver/certmagic v0.13.1 github.com/cretz/bine v0.1.1-0.20200124154328-f9f678b84cca github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f github.com/dgraph-io/ristretto v0.0.4-0.20210311064603-e4f298c8aa88 @@ -25,7 +25,6 @@ require ( github.com/gorilla/handlers v1.5.1 github.com/jonboulle/clockwork v0.2.2 // indirect github.com/joncrlsn/dque v0.0.0-20200702023911-3e80e3146ce5 - github.com/klauspost/cpuid v1.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/kyokomi/emoji/v2 v2.2.8 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible @@ -35,7 +34,7 @@ require ( github.com/magiconair/properties v1.8.5 // indirect github.com/mattn/go-sqlite3 v1.14.7 github.com/microcosm-cc/bluemonday v1.0.9 - github.com/miekg/dns v1.1.41 // indirect + github.com/miekg/dns v1.1.42 // indirect github.com/mitchellh/go-server-timing v1.0.1 github.com/mitchellh/mapstructure v1.4.1 // indirect github.com/pelletier/go-toml v1.9.0 // indirect @@ -54,14 +53,14 @@ require ( github.com/vcraescu/go-paginator v1.0.1-0.20201114172518-2cfc59fe05c2 github.com/yuin/goldmark v1.3.5 github.com/yuin/goldmark-emoji v1.0.1 - go.uber.org/multierr v1.6.0 // indirect + go.uber.org/multierr v1.7.0 // indirect go.uber.org/zap v1.16.0 // indirect - golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect + golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf // indirect golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect golang.org/x/mod v0.4.1 // indirect - golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 + golang.org/x/net v0.0.0-20210505214959-0714010a04ed golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20210426230700-d19ff857e887 // indirect + golang.org/x/sys v0.0.0-20210507014357-30e306a8bba5 // indirect golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/ini.v1 v1.62.0 // indirect diff --git a/go.sum b/go.sum index f4b2ac6..db13860 100644 --- a/go.sum +++ b/go.sum @@ -41,8 +41,8 @@ github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBW github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs= github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/bradfitz/gomemcache v0.0.0-20170208213004-1952afaa557d/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60= -github.com/caddyserver/certmagic v0.13.0 h1:ky0rntZvIFiUKFdIikYxj31WN+Ts0Od6Wjz83iTzxfc= -github.com/caddyserver/certmagic v0.13.0/go.mod h1:dNOzF4iOB7H9E51xTooMB90vs+2XNVtpnx0liQNsQY4= +github.com/caddyserver/certmagic v0.13.1 h1:A5qLxh9J6/CYWEOHaj135IWAjCY0193ONxEy8jbOlPw= +github.com/caddyserver/certmagic v0.13.1/go.mod h1:+zhQtEgLOyXRA/KRduHXNhGGdTeqRM4ePj8eBGD/2CQ= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= @@ -180,9 +180,8 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/cpuid v1.2.5/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= -github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s= -github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= +github.com/klauspost/cpuid/v2 v2.0.6 h1:dQ5ueTiftKxp0gyjKSx5+8BtPWkyQbd95m8Gys/RarI= +github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -232,8 +231,8 @@ github.com/microcosm-cc/bluemonday v1.0.9 h1:dpCwruVKoyrULicJwhuY76jB+nIxRVKv/e2 github.com/microcosm-cc/bluemonday v1.0.9/go.mod h1:B2riunDr9benLHghZB7hjIgdwSUzzs0pjCxFrWYEZFU= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= -github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/miekg/dns v1.1.42 h1:gWGe42RGaIqXQZ+r3WUGEKBEtvPHY2SXo4dqixDNxuY= +github.com/miekg/dns v1.1.42/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-server-timing v1.0.1 h1:f00/aIe8T3MrnLhQHu3tSWvnwc5GV/p5eutuu3hF/tE= @@ -352,8 +351,8 @@ go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= +go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= @@ -368,8 +367,8 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf h1:B2n+Zi5QeYRDAEodEu72OS36gmTWjgpXr2+cWcBW90o= +golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -416,8 +415,8 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210505214959-0714010a04ed h1:V9kAVxLvz1lkufatrpHuUVyJ/5tR3Ms7rk951P4mI98= +golang.org/x/net v0.0.0-20210505214959-0714010a04ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20170912212905-13449ad91cb2/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -454,8 +453,8 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887 h1:dXfMednGJh/SUUFjTLsWJz3P+TQt9qnR11GgeI3vWKs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210507014357-30e306a8bba5 h1:cez+MEm4+A0CG7ik1Qzj3bmK9DFoouuLom9lwM+Ijow= +golang.org/x/sys v0.0.0-20210507014357-30e306a8bba5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/healthcheck.go b/healthcheck.go index b2b8b01..c405f59 100644 --- a/healthcheck.go +++ b/healthcheck.go @@ -21,3 +21,11 @@ func healthcheck() bool { _, _ = io.Copy(io.Discard, resp.Body) return resp.StatusCode == 200 } + +func healthcheckExitCode() int { + if healthcheck() { + return 0 + } else { + return 1 + } +} diff --git a/http.go b/http.go index 64143d2..139096d 100644 --- a/http.go +++ b/http.go @@ -81,11 +81,7 @@ func startServer() (err error) { ReadTimeout: 5 * time.Minute, WriteTimeout: 5 * time.Minute, } - go onShutdown(func() { - toc, c := context.WithTimeout(context.Background(), 5*time.Second) - _ = s.Shutdown(toc) - c() - }) + addShutdownFunc(shutdownServer(s, "main server")) if appConfig.Server.PublicHTTPS { // Configure certmagic.Default.Storage = &certmagic.FileStorage{Path: "data/https"} @@ -98,11 +94,7 @@ func startServer() (err error) { ReadTimeout: 5 * time.Minute, WriteTimeout: 5 * time.Minute, } - go onShutdown(func() { - toc, c := context.WithTimeout(context.Background(), 5*time.Second) - _ = httpServer.Shutdown(toc) - c() - }) + addShutdownFunc(shutdownServer(httpServer, "http server")) go func() { if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Println("Failed to start HTTP server:", err.Error()) @@ -130,6 +122,15 @@ func startServer() (err error) { return nil } +func shutdownServer(s *http.Server, name string) func() { + return func() { + toc, c := context.WithTimeout(context.Background(), 5*time.Second) + _ = s.Shutdown(toc) + c() + log.Println("Stopped server:", name) + } +} + func redirectToHttps(w http.ResponseWriter, r *http.Request) { requestHost, _, err := net.SplitHostPort(r.Host) if err != nil { diff --git a/main.go b/main.go index 8a11330..0c4ab29 100644 --- a/main.go +++ b/main.go @@ -21,12 +21,12 @@ func main() { if *cpuprofile != "" { f, err := os.Create(*cpuprofile) if err != nil { - log.Fatal("could not create CPU profile: ", err) + log.Fatalln("could not create CPU profile: ", err) return } defer f.Close() if err := pprof.StartCPUProfile(f); err != nil { - log.Fatal("could not start CPU profile: ", err) + log.Fatalln("could not start CPU profile: ", err) return } defer pprof.StopCPUProfile() @@ -50,17 +50,16 @@ func main() { // Initialize config log.Println("Initialize configuration...") if err = initConfig(); err != nil { - log.Fatalln("Failed to init config:", err.Error()) + logErrAndQuit("Failed to init config:", err.Error()) + return } // Healthcheck tool if len(os.Args) >= 2 && os.Args[1] == "healthcheck" { // Connect to public address + "/ping" and exit with 0 when successful - if health := healthcheck(); health { - os.Exit(0) - } else { - os.Exit(1) - } + health := healthcheckExitCode() + shutdown() + os.Exit(health) return } @@ -71,10 +70,11 @@ func main() { AccountName: appConfig.User.Nick, }) if err != nil { - log.Fatalln(err.Error()) + logErrAndQuit(err.Error()) return } log.Println("TOTP-Secret:", key.Secret()) + shutdown() return } @@ -87,7 +87,7 @@ func main() { // Initialize database and markdown log.Println("Initialize database...") if err = initDatabase(); err != nil { - log.Fatalln("Failed to init database:", err.Error()) + logErrAndQuit("Failed to init database:", err.Error()) return } log.Println("Initialize server components...") @@ -96,45 +96,42 @@ func main() { // Link check tool after init of markdown if len(os.Args) >= 2 && os.Args[1] == "check" { checkAllExternalLinks() - if err = closeDb(); err != nil { - log.Fatalln("Failed to close DB:", err.Error()) - return - } + shutdown() return } // More initializations initMinify() if err = initTemplateAssets(); err != nil { // Needs minify - log.Fatalln("Failed to init template assets:", err.Error()) + logErrAndQuit("Failed to init template assets:", err.Error()) return } if err = initTemplateStrings(); err != nil { - log.Fatalln("Failed to init template translations:", err.Error()) + logErrAndQuit("Failed to init template translations:", err.Error()) return } if err = initRendering(); err != nil { // Needs assets and minify - log.Fatalln("Failed to init HTML rendering:", err.Error()) + logErrAndQuit("Failed to init HTML rendering:", err.Error()) return } if err = initCache(); err != nil { - log.Fatalln("Failed to init HTTP cache:", err.Error()) + logErrAndQuit("Failed to init HTTP cache:", err.Error()) return } if err = initRegexRedirects(); err != nil { - log.Fatalln("Failed to init redirects:", err.Error()) + logErrAndQuit("Failed to init redirects:", err.Error()) return } if err = initHTTPLog(); err != nil { - log.Fatal("Failed to init HTTP logging:", err.Error()) + logErrAndQuit("Failed to init HTTP logging:", err.Error()) return } if err = initActivityPub(); err != nil { - log.Fatalln("Failed to init ActivityPub:", err.Error()) + logErrAndQuit("Failed to init ActivityPub:", err.Error()) return } if err = initWebmention(); err != nil { - log.Fatalln("Failed to init webmention support:", err.Error()) + logErrAndQuit("Failed to init webmention support:", err.Error()) return } initTelegram() @@ -143,22 +140,20 @@ func main() { startHourlyHooks() // Start the server - log.Println("Starting server...") + log.Println("Starting server(s)...") err = startServer() if err != nil { - log.Fatalln("Failed to start server:", err.Error()) + logErrAndQuit("Failed to start server(s):", err.Error()) return } - log.Println("Stopped server(s)") // Wait till everything is shutdown waitForShutdown() - // Close DB - if err = closeDb(); err != nil { - log.Fatalln("Failed to close DB:", err.Error()) - return - } - log.Println("Closed Database") - +} + +func logErrAndQuit(v ...interface{}) { + log.Println(v...) + shutdown() + os.Exit(1) } diff --git a/shutdown.go b/shutdown.go index a5ed9ff..1b68593 100644 --- a/shutdown.go +++ b/shutdown.go @@ -7,15 +7,40 @@ import ( "syscall" ) -var shutdownWg sync.WaitGroup +var ( + quit = make(chan os.Signal, 1) + shutdownFuncs = []func(){} + shutdownWg sync.WaitGroup + shutdownFuncMapMutex sync.Mutex +) -func onShutdown(f func()) { - defer shutdownWg.Done() +func init() { + signal.Notify(quit, + os.Interrupt, + syscall.SIGINT, + syscall.SIGTERM, // e.g. Docker stop + ) + go func() { + <-quit + shutdown() + }() +} + +func addShutdownFunc(f func()) { shutdownWg.Add(1) - quit := make(chan os.Signal, 1) - signal.Notify(quit, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) - <-quit - f() + shutdownFuncMapMutex.Lock() + shutdownFuncs = append(shutdownFuncs, f) + shutdownFuncMapMutex.Unlock() +} + +func shutdown() { + for _, f := range shutdownFuncs { + go func(f func()) { + defer shutdownWg.Done() + f() + }(f) + } + shutdownWg.Wait() } func waitForShutdown() { diff --git a/tor.go b/tor.go index 18544d7..069a2df 100644 --- a/tor.go +++ b/tor.go @@ -81,11 +81,7 @@ func startOnionService(h http.Handler) error { ReadTimeout: 5 * time.Minute, WriteTimeout: 5 * time.Minute, } - go onShutdown(func() { - toc, c := context.WithTimeout(context.Background(), 5*time.Second) - _ = s.Shutdown(toc) - c() - }) + addShutdownFunc(shutdownServer(s, "tor")) if err = s.Serve(onion); err != nil && err != http.ErrServerClosed { return err }