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

WebSocket can lose the URL encoding of URI query parameters

    Details

    • Subcomponent:
    • Resolved In Build:
      b04

      Description

      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


        Attachments

          Activity

            People

            • Assignee:
              ryadav Rahul Yadav
              Reporter:
              webbuggrp Webbug Group
            • Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: