[bug]: Resource leak due to early returns in proxy cleanup functions
👀 Is there an existing issue for this?
- [x] I have searched and didn't find similar issue
👍 Current behavior
When Keploy attempts to stop the proxy server or clean up proxy connections, two cleanup functions return early on error, which prevents the rest of the shutdown logic from running.
In StopProxyServer(), if closing any client connection fails, the function returns immediately. This skips unlocking the mutex, stopping DNS servers, closing the listener, and closing the error channel.
Similarly, in handleIncomingConnection(), the deferred cleanup block returns early when srcConn.Close() or dstConn.Close() returns an error. As a result, the parser error group is never waited on, and some connections remain unclosed.
This leads to resource leaks, potential deadlocks, and inconsistent proxy shutdown.
What i expect: Cleanup should always complete, even if one step fails.
The proxy should:
- Unlock mutexes
- Close all connections
- Close listeners
- Stop DNS servers
- Close error channels
- Wait for parser goroutines
Errors should be logged, but cleanup should continue instead of returning early.
why this is critical:
- Causes resource leaks in production
- Can deadlock future proxy start/stop cycles
- Leaks DNS servers, network listeners, goroutines
- Common cleanup anti-pattern (return inside defer / cleanup loop)
- Affects stability of Keploy proxy mode
👟 Steps to Replicate
- Start Keploy proxy in a test environment.
- Simulate a client connection that fails during Close() (e.g., closed by another goroutine or forced failure in custom test).
- Call StopProxyServer().
- Observe:
- mutex remains locked
- DNS servers still running
- listener still bound
- error channel not closed
- goroutines left hanging
- Run a second proxy start/stop cycle → observe deadlock / leaked resources. or use the test provided in the repo to observe it more clearly . here is the exack terminal screenshot after running the test:
📜 Logs (if any)
No specific Keploy logs—error is visible by instrumenting Close() failures and observing skipped cleanup and hanging goroutines.
💻 Operating system
Linux
🧾 System Info (uname -a)
Linux (WSL2 Ubuntu environment)
📦 OS Release Info (cat /etc/os-release)
NAME="Ubuntu" VERSION="22.04 LTS"
🐳 Docker Info (if applicable)
Yes, running inside Docker.
- Docker version: 24.0.2
- Docker Desktop version: 4.24.2
- Build image: golang:1.21-alpine
- Runtime image: alpine:3.18
🧱 Your Environment
Running on local machine (WSL2) .
🎲 Version
Keploy 2.11.31
📦 Repository
keploy
🤔 What use case were you trying? (optional)
No response
@remo-lab Can I work on this issue ?
can I work on this issue ?
I want to take on this resource leak issue.
Here’s what I found digging into the bug in StopProxyServer() (lines 664-717):
- In lines 681-687, there’s a loop that tries to close
clientConnections. If anyClose()call fails, the code returns right away. - Because of that, a bunch of cleanup steps get skipped:
p.connMutex.Unlock()(line 688)p.Listener.Close()(line 692)p.stopDNSServers(ctx)(line 698)p.CloseErrorChannel()(line 704)
- The end result? You get a mutex deadlock, DNS servers leaking, the listener leaking, and the error channel leaking too.
I’m still trying to find handleIncomingConnection(). Once I track it down, I’ll dig into its pattern as well.
Here’s how I’ll fix it:
- Ditch the early returns. Instead, I’ll collect errors and log them.
- Make sure all cleanup steps run, even if some fail.
- Write solid tests to catch goroutine leaks.
- Maybe build a
CleanupManagerhelper to keep error handling tidy and consistent.
I’m gearing up for GSoC 2026 and plan to really focus on delivering a fix that’s ready for production.
Can you assign this issue to me?
(Quick side note: I’m still hunting for handleIncomingConnection(). I can’t find it in proxy.go - should I be looking in another file?)
@Mayanknishad9 The exact function name handleIncomingConnection() doesn't exist, but the relevant function is: handleConnection() at line 304 in pkg/agent/proxy/proxy.go This handles connections accepted by the proxy server (accepted at line 266, handled at line 287). These connections are stored in p.clientConnections, which StopProxyServer() attempts to close. There's also a separate handleConnection() in pkg/agent/proxy/incoming/incoming.go (line 165) for incoming HTTP/gRPC connections, but that's a different system. For this bug fix, focus on proxy.go and the handleConnection() function there.
For the issue assignment, you may also check with @officialasishkumar if he wants to keep this issue open or proceed with the fix as completed. Let me know if anything else is needed!
Thanks @remo-lab I'll check with @officialasishkumar about proceeding with the fix.
Summary of what I found-
StopProxyServer()(proxy.go): Early return in connection close loop skips mutex unlock, DNS stop, listener close, error channel closehandleConnection()(proxy.go): Early returns in defer block (lines 374 & 386) skipparserErrGrp.Wait(), causing goroutine leaks
Fix plan: Remove early returns, log errors but continue all cleanup steps. Awaiting confirmation from @officialasishkumar to proceed. Thanks!
@AkashKumar7902 sir please check this out then only i will start working on it.
You can start to work on it
@remo-lab The screenshot you have attached for test shows that test files are in ./pkg/agent/proxy but there are no such test file or any test function named "TestStopProxyServer_ResourceLeak".