jersey
jersey copied to clipboard
NettyConnector hangs indefinitely when there is no server listening on port
Observed behavior
When connecting to a port that has no server is listening on it, the NettyConnector
provider for the Jersey client hangs indefinitely. This seems to be a regression introduced between versions 3.0.8 and 3.1.2.
This is extremely straightforward to reproduce and does not seem to be a timing issue, because it happens every single time I issue an HTTP request without a server running.
Test case
The following code reproduces the issue with version 3.1.2 of Jersey (I'm using version 4.1.94.Final
of Netty):
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.netty.connector.NettyConnectorProvider;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.WebTarget;
public class NettyProviderHang {
public static void main(String... args) {
// configure NettyConnectionProvider and create client
ClientConfig config = new ClientConfig();
config.connectorProvider(new NettyConnectorProvider());
Client client = ClientBuilder.newClient(config);
try {
// issue HTTP request
WebTarget target = client.target("http://localhost:8080");
target.request().get();
} finally {
client.close();
}
}
}
This is the stacktrace for the hung thread:
Thread[main,5,main]
[email protected]/jdk.internal.misc.Unsafe.park(Native Method)
[email protected]/java.util.concurrent.locks.LockSupport.park(LockSupport.java:211)
[email protected]/java.util.concurrent.CompletableFuture$Signaller.block(CompletableFuture.java:1864)
[email protected]/java.util.concurrent.ForkJoinPool.unmanagedBlock(ForkJoinPool.java:3465)
[email protected]/java.util.concurrent.ForkJoinPool.managedBlock(ForkJoinPool.java:3436)
[email protected]/java.util.concurrent.CompletableFuture.waitingGet(CompletableFuture.java:1898)
[email protected]/java.util.concurrent.CompletableFuture.join(CompletableFuture.java:2117)
app//org.glassfish.jersey.netty.connector.NettyConnector.apply(NettyConnector.java:166)
app//org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:300)
app//org.glassfish.jersey.client.JerseyInvocation.lambda$invoke$0(JerseyInvocation.java:662)
app//org.glassfish.jersey.client.JerseyInvocation$$Lambda$207/0x000000014319c628.call(Unknown Source)
app//org.glassfish.jersey.client.JerseyInvocation.call(JerseyInvocation.java:697)
app//org.glassfish.jersey.client.JerseyInvocation.lambda$runInScope$3(JerseyInvocation.java:691)
app//org.glassfish.jersey.client.JerseyInvocation$$Lambda$208/0x000000014319c868.call(Unknown Source)
app//org.glassfish.jersey.internal.Errors.process(Errors.java:292)
app//org.glassfish.jersey.internal.Errors.process(Errors.java:274)
app//org.glassfish.jersey.internal.Errors.process(Errors.java:205)
app//org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:390)
app//org.glassfish.jersey.client.JerseyInvocation.runInScope(JerseyInvocation.java:691)
app//org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:661)
app//org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:413)
app//org.glassfish.jersey.client.JerseyInvocation$Builder.get(JerseyInvocation.java:313)
app//NettyProviderHang.main(NettyProviderHang.java:33)
Expected behavior
I would expect the client to fail immediately with a "Connection refused" error message. This is what happens on version 3.0.8 with the Netty connector (same version of Netty):
Exception in thread "main" jakarta.ws.rs.ProcessingException: Connection refused: localhost/127.0.0.1:8080
at org.glassfish.jersey.netty.connector.NettyConnector.apply(NettyConnector.java:170)
at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:297)
at org.glassfish.jersey.client.JerseyInvocation.lambda$invoke$0(JerseyInvocation.java:662)
at org.glassfish.jersey.client.JerseyInvocation.call(JerseyInvocation.java:697)
at org.glassfish.jersey.client.JerseyInvocation.lambda$runInScope$3(JerseyInvocation.java:691)
at org.glassfish.jersey.internal.Errors.process(Errors.java:292)
at org.glassfish.jersey.internal.Errors.process(Errors.java:274)
at org.glassfish.jersey.internal.Errors.process(Errors.java:205)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:390)
at org.glassfish.jersey.client.JerseyInvocation.runInScope(JerseyInvocation.java:691)
at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:661)
at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:413)
at org.glassfish.jersey.client.JerseyInvocation$Builder.get(JerseyInvocation.java:313)
at NettyProviderHang.main(NettyProviderHang.java:18)
Caused by: io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: localhost/127.0.0.1:8080
Caused by: java.net.ConnectException: Connection refused
at java.base/sun.nio.ch.Net.pollConnect(Native Method)
at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:672)
at java.base/sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:946)
at io.netty.channel.socket.nio.NioSocketChannel.doFinishConnect(NioSocketChannel.java:337)
at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:334)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:776)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:833)
When using version 3.1.2 of Jersey without the NettyConnector
(commenting out config.connectorProvider()
invocation) we also get the expected behavior:
Exception in thread "main" jakarta.ws.rs.ProcessingException: java.net.ConnectException: Connection refused
at org.glassfish.jersey.client.internal.HttpUrlConnector.apply(HttpUrlConnector.java:275)
at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:300)
at org.glassfish.jersey.client.JerseyInvocation.lambda$invoke$0(JerseyInvocation.java:662)
at org.glassfish.jersey.client.JerseyInvocation.call(JerseyInvocation.java:697)
at org.glassfish.jersey.client.JerseyInvocation.lambda$runInScope$3(JerseyInvocation.java:691)
at org.glassfish.jersey.internal.Errors.process(Errors.java:292)
at org.glassfish.jersey.internal.Errors.process(Errors.java:274)
at org.glassfish.jersey.internal.Errors.process(Errors.java:205)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:390)
at org.glassfish.jersey.client.JerseyInvocation.runInScope(JerseyInvocation.java:691)
at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:661)
at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:413)
at org.glassfish.jersey.client.JerseyInvocation$Builder.get(JerseyInvocation.java:313)
at NettyProviderHang.main(NettyProviderHang.java:17)
Caused by: java.net.ConnectException: Connection refused
at java.base/sun.nio.ch.Net.connect0(Native Method)
at java.base/sun.nio.ch.Net.connect(Net.java:579)
at java.base/sun.nio.ch.Net.connect(Net.java:568)
at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:593)
at java.base/java.net.Socket.connect(Socket.java:633)
at java.base/sun.net.NetworkClient.doConnect(NetworkClient.java:178)
at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:533)
at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:638)
at java.base/sun.net.www.http.HttpClient.<init>(HttpClient.java:281)
at java.base/sun.net.www.http.HttpClient.New(HttpClient.java:386)
at java.base/sun.net.www.http.HttpClient.New(HttpClient.java:408)
at java.base/sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:1309)
at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1242)
at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1128)
at java.base/sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:1057)
at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1665)
at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1589)
at java.base/java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:529)
at org.glassfish.jersey.client.internal.HttpUrlConnector._apply(HttpUrlConnector.java:409)
at org.glassfish.jersey.client.internal.HttpUrlConnector.apply(HttpUrlConnector.java:273)
... 13 more
Environment
Java 17 Jersey 3.1.2 Netty 4.1.94.Final