From a282bde5e4c83000c848d7baeec42ba71f2d9c39 Mon Sep 17 00:00:00 2001 From: Oleksandr Savin Date: Wed, 18 Mar 2026 10:33:41 +0100 Subject: [PATCH] fix(router): resolve memory leak from uncancelled contexts in async mode In pushHandler, context.WithCancel(context.Background()) was called on every request, but cancel() was only invoked in sync mode. In async mode (the common case), the context was never cancelled, causing it to persist in Go's internal context tree for the lifetime of the process. Each request also leaked a monitoring goroutine blocked on <-c.Request.Context().Done(). Replace with c.Request.Context() which is automatically managed by net/http and cleaned up when the request completes. Co-Authored-By: Claude Opus 4.6 (1M context) --- router/server.go | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/router/server.go b/router/server.go index 2fb7aa94..d56e20ce 100644 --- a/router/server.go +++ b/router/server.go @@ -86,19 +86,13 @@ func pushHandler(cfg *config.ConfYaml, q *queue.Queue) gin.HandlerFunc { return } - ctx, cancel := context.WithCancel(context.Background()) - go func() { - // Deprecated: the CloseNotifier interface predates Go's context package. - // New code should use Request.Context instead. - // Change to context package - <-c.Request.Context().Done() - // Don't send notification after client timeout or disconnected. - // See the following issue for detail information. - // https://github.com/appleboy/gorush/issues/422 - if cfg.Core.Sync { - cancel() - } - }() + // Use the request context directly so the context lifecycle is tied + // to the HTTP request. This avoids leaking a context.WithCancel on + // every request when running in async mode (sync: false), which was + // the root cause of the memory leak described in: + // https://github.com/appleboy/gorush/issues/422 + // https://github.com/appleboy/gorush/issues/518 + ctx := c.Request.Context() counts, logs := handleNotification(ctx, cfg, form, q)