2424/**
2525 * Tests for WebSocket support with Java HttpClient.
2626 *
27- * This test demonstrates the issue from JDK-8361305 where:
28- * - Direct WebSocket connection (ws://) works fine
29- * - But WebSocket upgrade after HTTP connection to the same endpoint does NOT work properly
27+ * This test demonstrates the issue from JDK-8361305 where: - Direct WebSocket
28+ * connection (ws://) works fine - But WebSocket upgrade after HTTP connection
29+ * to the same endpoint does NOT work properly
3030 */
3131public class JavaHttpClientWebSocketTest extends JavaHttpClientBase {
3232
3333 private static final Logger logger = LoggerFactory .getLogger (JavaHttpClientWebSocketTest .class );
34-
34+
3535 private static NettyWebSocketServer wsServer ;
3636
3737 @ BeforeAll
@@ -51,166 +51,89 @@ public static void stopServers() {
5151 @ Test
5252 @ DisplayName ("Direct WebSocket connection works (ws:// URI)" )
5353 public void testDirectWebSocketConnection () throws Exception {
54- logger . info ( " \n === Testing Direct WebSocket Connection ===" );
55-
56- HttpClient client = HttpClient .newHttpClient ();
54+ // TODO we want to test what happens if the server ONLY support Webservice
55+ // protocol!
56+ HttpClient client = HttpClient .newBuilder (). connectTimeout ( Duration . ofSeconds ( 10 )). build ();
5757 WebSocket .Builder webSocketBuilder = client .newWebSocketBuilder ();
58-
59- URI wsUri = URI .create ("ws://localhost:" + wsServer .getPort () + "/websocket" );
60- logger .info ("Connecting to WebSocket URI: {}" , wsUri );
61-
58+ }
59+
60+ @ Test
61+ @ DisplayName ("WebSocket upgrade through HTTP" )
62+ public void testWebSocketUpgradeAfterHttp2 () throws Exception {
63+ logger .info ("\n === Testing WebSocket Upgrade from HTTP Connection ===" );
64+
65+ HttpClient client = HttpClient .newBuilder ().version (HttpClient .Version .HTTP_2 )
66+ .connectTimeout (Duration .ofSeconds (10 )).build ();
67+
68+ WebSocket .Builder webSocketBuilder = client .newWebSocketBuilder ();
69+ URI wsUri = URI .create ("ws://localhost:" + wsServer .getPort () + "/http" );
70+ logger .info ("Step 2: Attempting WebSocket connection to same endpoint: {}" , wsUri );
71+
6272 CountDownLatch messageLatch = new CountDownLatch (1 );
6373 AtomicReference <String > receivedMessage = new AtomicReference <>();
64-
74+ AtomicReference <Throwable > errorRef = new AtomicReference <>();
75+
6576 WebSocket .Listener listener = new WebSocket .Listener () {
6677 @ Override
6778 public CompletionStage <?> onText (WebSocket webSocket , CharSequence data , boolean last ) {
68- logger .info ("Received message: {}" , data );
79+ logger .info ("Received WebSocket message: {}" , data );
6980 receivedMessage .set (data .toString ());
7081 messageLatch .countDown ();
7182 return CompletableFuture .completedFuture (null );
7283 }
73-
84+
7485 @ Override
7586 public void onOpen (WebSocket webSocket ) {
76- logger .info ("WebSocket connection opened" );
87+ logger .info ("WebSocket connection opened successfully " );
7788 WebSocket .Listener .super .onOpen (webSocket );
7889 }
79-
90+
8091 @ Override
8192 public CompletionStage <?> onClose (WebSocket webSocket , int statusCode , String reason ) {
8293 logger .info ("WebSocket closed with status: {}, reason: {}" , statusCode , reason );
8394 return CompletableFuture .completedFuture (null );
8495 }
85-
96+
8697 @ Override
8798 public void onError (WebSocket webSocket , Throwable error ) {
8899 logger .error ("WebSocket error" , error );
100+ errorRef .set (error );
89101 }
90102 };
91-
92- CompletableFuture <WebSocket > wsFuture = webSocketBuilder .buildAsync (wsUri , listener );
93- WebSocket webSocket = wsFuture .get (10 , TimeUnit .SECONDS );
94- assertNotNull (webSocket , "WebSocket should be established" );
95-
96- // Send a message
97- webSocket .sendText ("Hello from client" , true ).get (5 , TimeUnit .SECONDS );
98-
99- // Wait for response
100- boolean received = messageLatch .await (10 , TimeUnit .SECONDS );
101- assertTrue (received , "Should receive a response from WebSocket server" );
102- assertEquals ("Echo: Hello from client" , receivedMessage .get (), "Should receive echoed message" );
103-
104- // Close the connection
105- webSocket .sendClose (WebSocket .NORMAL_CLOSURE , "Closing" ).get (5 , TimeUnit .SECONDS );
106-
107- logger .info ("=== Direct WebSocket Connection test completed successfully ===\n " );
108- }
109103
110- @ Test
111- @ DisplayName ("WebSocket upgrade after HTTP/2 connection does NOT work (demonstrates JDK-8361305)" )
112- public void testWebSocketUpgradeAfterHttp2 () throws Exception {
113- logger .info ("\n === Testing WebSocket Upgrade after HTTP/2 Connection ===" );
114-
115- // Create a combined server that supports both HTTP/2 upgrade and WebSocket on same port
116- NettyWebSocketServer combinedServer = new NettyWebSocketServer (9999 );
117-
118104 try {
119- // Create HttpClient configured for HTTP/2
120- HttpClient client = HttpClient .newBuilder ()
121- .version (HttpClient .Version .HTTP_2 )
122- .connectTimeout (Duration .ofSeconds (10 ))
123- .build ();
124-
125- // Step 1: First make a regular HTTP request which may establish HTTP/2 connection
126- // This simulates a scenario where the client has already connected to the server
127- java .net .http .HttpRequest httpRequest = java .net .http .HttpRequest .newBuilder ()
128- .uri (URI .create ("http://localhost:" +combinedServer .getPort ()+"/websocket" ))
129- .GET ()
130- .build ();
131-
132- logger .info ("Step 1: Making HTTP request to http://localhost:9090/websocket" );
133- try {
134- java .net .http .HttpResponse <String > httpResponse = client .send (httpRequest ,
135- java .net .http .HttpResponse .BodyHandlers .ofString ());
136- logger .info ("HTTP Response status: {}, version: {}" , httpResponse .statusCode (), httpResponse .version ());
137- } catch (Exception e ) {
138- logger .info ("HTTP request completed (or failed): {}" , e .getMessage ());
139- }
140-
141- // Step 2: Now try to establish WebSocket connection to the SAME endpoint
142- // This is where JDK-8361305 issue manifests - trying to upgrade to WebSocket
143- // after HTTP/2 connection exists
144- WebSocket .Builder webSocketBuilder = client .newWebSocketBuilder ();
145- URI wsUri = URI .create ("ws://localhost:" + combinedServer .getPort () + "/websocket" );
146- logger .info ("Step 2: Attempting WebSocket connection to same endpoint: {}" , wsUri );
147-
148- CountDownLatch messageLatch = new CountDownLatch (1 );
149- AtomicReference <String > receivedMessage = new AtomicReference <>();
150- AtomicReference <Throwable > errorRef = new AtomicReference <>();
151-
152- WebSocket .Listener listener = new WebSocket .Listener () {
153- @ Override
154- public CompletionStage <?> onText (WebSocket webSocket , CharSequence data , boolean last ) {
155- logger .info ("Received WebSocket message: {}" , data );
156- receivedMessage .set (data .toString ());
157- messageLatch .countDown ();
158- return CompletableFuture .completedFuture (null );
159- }
160-
161- @ Override
162- public void onOpen (WebSocket webSocket ) {
163- logger .info ("WebSocket connection opened successfully" );
164- WebSocket .Listener .super .onOpen (webSocket );
165- }
166-
167- @ Override
168- public CompletionStage <?> onClose (WebSocket webSocket , int statusCode , String reason ) {
169- logger .info ("WebSocket closed with status: {}, reason: {}" , statusCode , reason );
170- return CompletableFuture .completedFuture (null );
171- }
172-
173- @ Override
174- public void onError (WebSocket webSocket , Throwable error ) {
175- logger .error ("WebSocket error" , error );
176- errorRef .set (error );
177- }
178- };
179-
180- try {
181- CompletableFuture <WebSocket > wsFuture = webSocketBuilder .buildAsync (wsUri , listener );
182- WebSocket webSocket = wsFuture .get (10 , TimeUnit .SECONDS );
183-
184- // If we get here, the connection worked
185- assertNotNull (webSocket , "WebSocket should be established" );
186- logger .info ("WebSocket connection established successfully" );
187-
188- // Send a test message
189- webSocket .sendText ("Test message" , true ).get (5 , TimeUnit .SECONDS );
190-
191- // Wait for response
192- boolean received = messageLatch .await (10 , TimeUnit .SECONDS );
193- assertTrue (received , "Should receive echo response from WebSocket server" );
194- assertEquals ("Echo: Test message" , receivedMessage .get (), "Should receive echoed message" );
195-
196- // Assert that WebSocket upgrade was actually performed
197- combinedServer .assertWebSocketUpgrade ();
198-
199- // Close the connection
200- webSocket .sendClose (WebSocket .NORMAL_CLOSURE , "Test complete" ).get (5 , TimeUnit .SECONDS );
201-
202- logger .info ("SUCCESS: WebSocket connection worked after HTTP/2. Issue may be fixed in this Java version." );
203- } catch (Exception e ) {
204- // This documents the JDK-8361305 issue
205- logger .error ("EXPECTED FAILURE per JDK-8361305: WebSocket upgrade failed after HTTP/2 connection" , e );
206- logger .info ("This test documents that WebSocket upgrade does NOT work when HTTP/2 connection exists to the same host." );
207- // Don't fail the test - this documents the known issue
208- // In future Java versions where this is fixed, this catch block won't be reached
209- }
210- } finally {
211- combinedServer .stop ();
105+ CompletableFuture <WebSocket > wsFuture = webSocketBuilder .buildAsync (wsUri , listener );
106+ WebSocket webSocket = wsFuture .get (10 , TimeUnit .SECONDS );
107+
108+ // If we get here, the connection worked
109+ assertNotNull (webSocket , "WebSocket should be established" );
110+ logger .info ("WebSocket connection established successfully" );
111+
112+ // Send a test message
113+ webSocket .sendText ("Test message" , true ).get (5 , TimeUnit .SECONDS );
114+
115+ // Wait for response
116+ boolean received = messageLatch .await (10 , TimeUnit .SECONDS );
117+ assertTrue (received , "Should receive echo response from WebSocket server" );
118+ assertEquals ("Echo: Test message" , receivedMessage .get (), "Should receive echoed message" );
119+
120+ // Assert that WebSocket upgrade was actually performed
121+ wsServer .assertWebSocketUpgrade ();
122+
123+ // Close the connection
124+ webSocket .sendClose (WebSocket .NORMAL_CLOSURE , "Test complete" ).get (5 , TimeUnit .SECONDS );
125+
126+ logger .info ("SUCCESS: WebSocket connection worked after HTTP/2. Issue may be fixed in this Java version." );
127+ } catch (Exception e ) {
128+ // This documents the JDK-8361305 issue
129+ logger .error ("EXPECTED FAILURE per JDK-8361305: WebSocket upgrade failed after HTTP/2 connection" , e );
130+ logger .info (
131+ "This test documents that WebSocket upgrade does NOT work when HTTP/2 connection exists to the same host." );
132+ // Don't fail the test - this documents the known issue
133+ // In future Java versions where this is fixed, this catch block won't be
134+ // reached
212135 }
213-
136+
214137 logger .info ("=== WebSocket Upgrade test completed ===\n " );
215138 }
216139}
0 commit comments