keycloak: ProcessingException: RESTEASY004655: Unable to invoke request: java.net.SocketException: Connection reset
Describe the bug
I get a reset error when I use keycloak to search for users. This error does not appear every time, only occasionally, but I don’t know how to fix it. Please help~
private boolean verifyAccountFromKeycloak(String account, String email) {
final UsersResource usersResource = keycloak.realm(currentRealm).users();
if (CollectionUtils.isNotEmpty(usersResource.search(account, true))) {
return false;
}
List<UserRepresentation> users2 = usersResource.search(null, null, null, email, 0, 20);
return CollectionUtils.isEmpty(users2) || !users2.stream().anyMatch(s -> email.equals(s.getEmail()));
}
Version
15.0.2
Expected behavior
No errors or exceptions
Actual behavior
javax.ws.rs.ProcessingException: RESTEASY004655: Unable to invoke request: java.net.SocketException: Connection reset
at org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine.invoke(ApacheHttpClient4Engine.java:328)
at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.invoke(ClientInvocation.java:443)
at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invokeSync(ClientInvoker.java:149)
at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invoke(ClientInvoker.java:112)
at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientProxy.invoke(ClientProxy.java:76)
at com.sun.proxy.$Proxy196.refreshToken(Unknown Source)
at org.keycloak.admin.client.token.TokenManager.refreshToken(TokenManager.java:111)
at org.keycloak.admin.client.token.TokenManager.getAccessToken(TokenManager.java:72)
at org.keycloak.admin.client.token.TokenManager.getAccessTokenString(TokenManager.java:65)
at org.keycloak.admin.client.resource.BearerAuthFilter.filter(BearerAuthFilter.java:52)
at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.filterRequest(ClientInvocation.java:579)
at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.invoke(ClientInvocation.java:440)
at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invokeSync(ClientInvoker.java:149)
at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invoke(ClientInvoker.java:112)
at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientProxy.invoke(ClientProxy.java:76)
at com.sun.proxy.$Proxy249.search(Unknown Source)
at com.commercial.provider.service.impl.UserServiceImpl.lambda$verifyAccountFromKeycloak$0(UserServiceImpl.java:133)
at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590)
... 1 common frames omitted
Caused by: java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(SocketInputStream.java:210)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.security.ssl.InputRecord.readFully(InputRecord.java:465)
at sun.security.ssl.InputRecord.read(InputRecord.java:503)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:975)
at sun.security.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:933)
at sun.security.ssl.AppInputStream.read(AppInputStream.java:105)
at org.apache.http.impl.io.SessionInputBufferImpl.streamRead(SessionInputBufferImpl.java:137)
at org.apache.http.impl.io.SessionInputBufferImpl.fillBuffer(SessionInputBufferImpl.java:153)
at org.apache.http.impl.io.SessionInputBufferImpl.readLine(SessionInputBufferImpl.java:280)
at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:138)
at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:56)
at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:259)
at org.apache.http.impl.DefaultBHttpClientConnection.receiveResponseHeader(DefaultBHttpClientConnection.java:163)
at org.apache.http.impl.conn.CPoolProxy.receiveResponseHeader(CPoolProxy.java:157)
at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:273)
at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:125)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:272)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
at org.apache.http.impl.client.InternalHttpClient.doExecute$original$3AyxwFtD(InternalHttpClient.java:185)
at org.apache.http.impl.client.InternalHttpClient.doExecute$original$3AyxwFtD$accessor$QdjClIoY(InternalHttpClient.java)
at org.apache.http.impl.client.InternalHttpClient$auxiliary$wWrcgHZe.call(Unknown Source)
at org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstMethodsInter.intercept(InstMethodsInter.java:86)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
at org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine.invoke(ApacheHttpClient4Engine.java:323)
... 18 common frames omitted
How to Reproduce?
Sorry,This error does not appear every time, only occasionally.
Anything else?
Spring Boot: 2.3.11.RELEASE JDK Image: openjdk:8u212-jre-slim
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Comments: 16 (7 by maintainers)
I was able to reliable reproduce the issue using the latest keycloak and keycloak-admin-client (each 22.0.5).
Reproducer setup
I am running on Ubuntu 20.04 (WSL2 on Windows) and OpenJDK 17.
docker run -p 8081:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:22.0.5 start-devtcpkillsend RST packets to connections on port 8081 (you may need tosudo apt install dsniff):sudo tcpkill -1 -i lo port 8081keycloak-admin-clientand run this reproducer:The reproducer may take a few iterations until it hits this exception and exits:
Some iterations may fully succeed, some may only print this message:
I believe the differences are due to
tcpkillworking on a best-effort and timing based approach to inject RST packets, which may arrive at the application sooner or later. The desicionmaking whether a request is retried lives inside theDefaultHttpRequestRetryHandler. I observed thatretryRequest(the method that decides whether to retry a request) ends with this snippet of code:The code always reaches that statement. I saw that
this.requestSentRetryEnabledis always false, and the deciding factor isclientContext.isRequestSent(). My conclusion is that the POST request to refresh the token has been fully sent before aRSTpacket was received, resulting in the retry mechanism not retrying the request because it might have been processed by the server and can not be assumed to be idempotent.Possible solutions
Assuming the troublemaker is some network component closing idle connections, the problem can be avoided by auto-closing idle connections and/or testing pooled connections before use. This is what @tramiaczek suggested in his previous comment https://github.com/keycloak/keycloak/issues/8917#issuecomment-1712895984
A minimal configuration to enable auto-closing of connections may look like this:
For this specific usecase, namely the keycloak-admin-client transparently refreshing its token, it might be useful for it to automatically retry this too, since other connection failures are typically retried as well. Though I am unsure how this would be done.
Alternatively, application-side retry mechanisms may be considered. Given the keycloak admin client may throw connection errors at any time anyway, applications should probably be robust against this.
Yes, it currently works almost 2 months and the error was not reopened by the client.
We have the problem reported from the client’s environment - random
java.net.SocketException: Connection resetwhile executing get user operation (in most cases). Our scenario was" create a user (through Keycloak API) and generate some OTP for it (OTP generation code was not related to the Keycloak API, but we needed to get user Id from Keycloak to put it as a reference in our database). Keycloak and the service that invoked the API were deployed as separate pod’s in the same Kubernetes namespace - the client’s TEST/UAT env and our DEV environments were pretty the same (client followed our instructions to prepare env).Of course as we could not reproduce the issue, we tried to seek for the solution in a blind manner - we tried to use the latest possible version of
keycloak-admin-clientlibrary, but it did not help. So we thought that we should have as wide control over the HTTP client as possible and prepared the workaround described above. Our first idea was to turn on some DEBUG logs and observe how the connection pool or timeouts behave on client’s env, especially when the problem occurs again. But it did not occur… and the client closed the issue in Jira 😃 As client swears that he did not change enything in the environment, we suppose that some idle connection eviction/validation mechanims resolved the problem.I have the same issue, but possibly found the workaround (it is under tests now).
KeycloakBuilderhas a method:.resteasyClient(..)to provide custom implementation ofjavax.ws.rs.client.Clientand that implementation could have some settings about connection/read timeouts, evicting idle connections policy and retry handler implementation. Spring configuration forKeycloakbean could look like the following:I think that pooling settings like retry handler and idle connections eviction mechanism could cause that sockets used to connect through HTTP protocol should be tested before use and connections closed by some external mechanisms like firewall would be rejected by KeycloakAdmin client.