Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8245245

WebSocket can lose the URL encoding of URI query parameters

XMLWordPrintable

    • b04
    • Not verified

        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


              ryadav Rahul Yadav
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              6 Start watching this issue

                Created:
                Updated:
                Resolved: