-
Notifications
You must be signed in to change notification settings - Fork 684
Fix HttpClient resource leak in HTTP transports #610
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Fix HttpClient resource leak in HTTP transports #610
Conversation
- Add support for external HttpClient instances in HttpClientStreamableHttpTransport and HttpClientSseClientTransport builders - Implement proper HttpClient resource cleanup using reflection to close SelectorManager threads - Add shouldCloseHttpClient flag to control resource management lifecycle - Prevent thread leaks caused by unclosed HttpClient instances created via HttpClient.Builder.build() - Add comprehensive tests for external HttpClient usage and resource cleanup Fixes thread accumulation issue where HttpClient-xxxx-SelectorManager threads would continuously grow, leading to memory exhaustion. This addresses the underlying JDK issue documented in JDK-8308364. Related: https://bugs.openjdk.org/browse/JDK-8308364
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @TsengX 👋
Thanks for the report, and for the proposed fix. A few notes on this PR:
- On the
HttpClientImpl
, from JDK 21 onwards, there is a#close()
method, and we should try to use this is available:We'd only fall back to reflection if there's nofor (var method : httpClient.getClass().getMethods()) { if (method.getName().equals("close")) { method.invoke(httpClient); return; } }
close
. - Do you have a strong case for passing an HTTP client from the outside? It'd like to keep the API surface smaller, and not have conflicting
HttpClient.Builder
vsHttpClient
. - If there's a strong case for the point above, I would prefer having a
Consumer<HttpClient>
in the builder for "on close" operations rather than a boolean. It would default to the "close client resources" implementation you proposed (or callingclose
if available).
// External HttpClient should still be usable after transport closes | ||
// (This is a basic test - in practice you'd verify the client is still | ||
// functional) | ||
assertThat(externalHttpClient).isNotNull(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[issue] This test passes even if the client is closed.
}); | ||
|
||
// This test verifies that internal HttpClient resources are cleaned up | ||
// The actual verification happens during the graceful close process |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[suggestion] We should try to make an HTTP request here and ensure the client is closed.
This pull request addresses the HttpClient resource leak issue described in #620.
Fixes #620
Fixes thread accumulation issue where HttpClient-xxxx-SelectorManager threads would continuously grow, leading to memory exhaustion. This addresses the underlying JDK issue documented in JDK-8308364.
Related: https://bugs.openjdk.org/browse/JDK-8308364
Motivation and Context
The
HttpClientStreamableHttpTransport
andHttpClientSseClientTransport
classes create new HttpClient instances viaHttpClient.Builder.build()
without properly managing their lifecycle. This leads to accumulation ofHttpClient-xxxx-SelectorManager
threads that are never cleaned up, eventually causing memory exhaustion.Root cause analysis traced this to the OpenJDK 17 HttpClientImpl implementation (JDK-8308364), where each HttpClient spawns dedicated SelectorManager threads for network I/O but lacks public APIs for proper resource cleanup.
This change introduces two solutions:
References
How Has This Been Tested?
HttpClientStreamableHttpSyncClientTests
andHttpSseMcpSyncClientTests
Breaking Changes
No breaking changes. This is an additive change that maintains full API compatibility. Existing code will continue to work as before, but will now benefit from proper resource management and the option to use external HttpClient instances.
Types of changes
Checklist
Additional context