-
Bug
-
Resolution: Fixed
-
P4
-
11, 14, 15
-
b04
-
Not verified
Issue | Fix Version | Assignee | Priority | Status | Resolution | Resolved In Build |
---|---|---|---|---|---|---|
JDK-8299381 | 11.0.19 | Paul Hohensee | P4 | Resolved | Fixed | b01 |
A DESCRIPTION OF THE PROBLEM :
I'm writing a simple client for a SignalR (WebSocket) based server using the newer java.net.http APIs. To setup a SignalR connection the client makes a standard HTTP call which generates a base64 encoded token, then this token is passed as a query parameter to setup the WebSocket.
The base64 token can contain + / = characters, which are also URI reserved characters so should be URL encoded to prevent confusion. Creating a WebSocket from a ws: URI with a base64 token that has been correctly URL encoded would fail repeatedly (a very rare successful connection caused a lot of head
scratching, very occasionally a token would be generated that didn't contain reserved characters).
Going though the JDK code I found the createRequestURI method in jdk.internal.net.http.websocket.OpeningHandshake. In this method the URI schema is rewritten from ws/wss to http/https. In the process the path and query parts are decoded and then used to create a new URI, and the original URI encoding is lost.
I was able to workaround the issue by modifying the createRequestURI method to do a simple string replacement on the ws/wss schema, and rebuilding the JDK.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Minimal sample file using jdk.httpserver to print the requested URI, from a simple GET request and WebSocket request.
javac WebSocketTest.java
java WebSocketTest
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
http://localhost:8000/?raw=abc+def/ghi=xyz&encoded=abc%2Bdef%2Fghi%3Dxyz
Server RequestURI: /?raw=abc+def/ghi=xyz&encoded=abc%2Bdef%2Fghi%3Dxyz
WebSocket Request
ws://localhost:8000/?&raw=abc+def/ghi=xyz&encoded=abc%2Bdef%2Fghi%3Dxyz
Server RequestURI: /?&raw=abc+def/ghi=xyz&encoded=abc%2Bdef%2Fghi%3Dxyz
ACTUAL -
http://localhost:8000/?raw=abc+def/ghi=xyz&encoded=abc%2Bdef%2Fghi%3Dxyz
Server RequestURI: /?raw=abc+def/ghi=xyz&encoded=abc%2Bdef%2Fghi%3Dxyz
WebSocket Request
ws://localhost:8000/?&raw=abc+def/ghi=xyz&encoded=abc%2Bdef%2Fghi%3Dxyz
Server RequestURI: /?&raw=abc+def/ghi=xyz&encoded=abc+def/ghi=xyz
bob@thinkpad:~/src/jigomo/trading-bittrex/src/main/java$
---------- BEGIN SOURCE ----------
WebSocketTest.java
-----
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.WebSocket;
import java.nio.charset.StandardCharsets;
public class WebSocketTest {
public static void main(String[] args) throws IOException, InterruptedException {
final var httpServer = HttpServer.create(new InetSocketAddress("localhost", 8000), 0);
httpServer.createContext("/", (exchange) -> {
System.out.println("Server RequestURI: " + exchange.getRequestURI());
exchange.sendResponseHeaders(200, -1);
exchange.close();
});
httpServer.start();
final var httpClient = HttpClient.newHttpClient();
final var raw = "abc+def/ghi=xyz";
System.out.println("Http Request");
final var httpURI = URI.create("http://localhost:8000/?raw=" + raw + "&encoded=" + URLEncoder.encode(raw, StandardCharsets.UTF_8));
System.out.println(httpURI);
httpClient.send(HttpRequest.newBuilder(httpURI).build(), HttpResponse.BodyHandlers.discarding());
Thread.sleep(1000L);
System.out.println("WebSocket Request");
final var wsURI = URI.create("ws://localhost:8000/?&raw=" + raw + "&encoded=" + URLEncoder.encode(raw, StandardCharsets.UTF_8));
System.out.println(wsURI);
final var webSocket = httpClient.newWebSocketBuilder().buildAsync(wsURI, new WebSocket.Listener() {});
Thread.sleep(1000L);
httpServer.stop(0);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
In the createRequestURI method to do a simple string replacement on the ws/wss schema.
FREQUENCY : always
I'm writing a simple client for a SignalR (WebSocket) based server using the newer java.net.http APIs. To setup a SignalR connection the client makes a standard HTTP call which generates a base64 encoded token, then this token is passed as a query parameter to setup the WebSocket.
The base64 token can contain + / = characters, which are also URI reserved characters so should be URL encoded to prevent confusion. Creating a WebSocket from a ws: URI with a base64 token that has been correctly URL encoded would fail repeatedly (a very rare successful connection caused a lot of head
scratching, very occasionally a token would be generated that didn't contain reserved characters).
Going though the JDK code I found the createRequestURI method in jdk.internal.net.http.websocket.OpeningHandshake. In this method the URI schema is rewritten from ws/wss to http/https. In the process the path and query parts are decoded and then used to create a new URI, and the original URI encoding is lost.
I was able to workaround the issue by modifying the createRequestURI method to do a simple string replacement on the ws/wss schema, and rebuilding the JDK.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Minimal sample file using jdk.httpserver to print the requested URI, from a simple GET request and WebSocket request.
javac WebSocketTest.java
java WebSocketTest
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
http://localhost:8000/?raw=abc+def/ghi=xyz&encoded=abc%2Bdef%2Fghi%3Dxyz
Server RequestURI: /?raw=abc+def/ghi=xyz&encoded=abc%2Bdef%2Fghi%3Dxyz
WebSocket Request
ws://localhost:8000/?&raw=abc+def/ghi=xyz&encoded=abc%2Bdef%2Fghi%3Dxyz
Server RequestURI: /?&raw=abc+def/ghi=xyz&encoded=abc%2Bdef%2Fghi%3Dxyz
ACTUAL -
http://localhost:8000/?raw=abc+def/ghi=xyz&encoded=abc%2Bdef%2Fghi%3Dxyz
Server RequestURI: /?raw=abc+def/ghi=xyz&encoded=abc%2Bdef%2Fghi%3Dxyz
WebSocket Request
ws://localhost:8000/?&raw=abc+def/ghi=xyz&encoded=abc%2Bdef%2Fghi%3Dxyz
Server RequestURI: /?&raw=abc+def/ghi=xyz&encoded=abc+def/ghi=xyz
bob@thinkpad:~/src/jigomo/trading-bittrex/src/main/java$
---------- BEGIN SOURCE ----------
WebSocketTest.java
-----
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.WebSocket;
import java.nio.charset.StandardCharsets;
public class WebSocketTest {
public static void main(String[] args) throws IOException, InterruptedException {
final var httpServer = HttpServer.create(new InetSocketAddress("localhost", 8000), 0);
httpServer.createContext("/", (exchange) -> {
System.out.println("Server RequestURI: " + exchange.getRequestURI());
exchange.sendResponseHeaders(200, -1);
exchange.close();
});
httpServer.start();
final var httpClient = HttpClient.newHttpClient();
final var raw = "abc+def/ghi=xyz";
System.out.println("Http Request");
final var httpURI = URI.create("http://localhost:8000/?raw=" + raw + "&encoded=" + URLEncoder.encode(raw, StandardCharsets.UTF_8));
System.out.println(httpURI);
httpClient.send(HttpRequest.newBuilder(httpURI).build(), HttpResponse.BodyHandlers.discarding());
Thread.sleep(1000L);
System.out.println("WebSocket Request");
final var wsURI = URI.create("ws://localhost:8000/?&raw=" + raw + "&encoded=" + URLEncoder.encode(raw, StandardCharsets.UTF_8));
System.out.println(wsURI);
final var webSocket = httpClient.newWebSocketBuilder().buildAsync(wsURI, new WebSocket.Listener() {});
Thread.sleep(1000L);
httpServer.stop(0);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
In the createRequestURI method to do a simple string replacement on the ws/wss schema.
FREQUENCY : always
- backported by
-
JDK-8299381 WebSocket can lose the URL encoding of URI query parameters
- Resolved