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

JDK9 WebSocket requireAbsent(headers, HEADER_VERSION) throws HandshakeException

    XMLWordPrintable

    Details

    • Subcomponent:
    • CPU:
      x86_64
    • OS:
      linux_ubuntu

      Description

      FULL PRODUCT VERSION :
      jdk 9.0.01

      ADDITIONAL OS VERSION INFORMATION :
      Linux jackonWD1T 4.10.0-38-generic #42-Ubuntu SMP Tue Oct 10 13:24:27 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux


      A DESCRIPTION OF THE PROBLEM :
      the websocket provided in jdk.incubator.httpclient throws the following exception when I connect to a websocket server (wss).

      java.util.concurrent.CompletionException: jdk.incubator.http.WebSocketHandshakeException
      at java.base/java.util.concurrent.CompletableFuture.encodeRelay(CompletableFuture.java:367)
      at java.base/java.util.concurrent.CompletableFuture.completeRelay(CompletableFuture.java:376)
      at java.base/java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:1074)
      at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:506)
      at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2073)
      at jdk.incubator.httpclient/jdk.incubator.http.ResponseProcessors$NullProcessor.onComplete(ResponseProcessors.java:336)
      at jdk.incubator.httpclient/jdk.incubator.http.BlockingPushPublisher.acceptData(BlockingPushPublisher.java:65)
      at jdk.incubator.httpclient/jdk.incubator.http.AbstractPushPublisher.consume(AbstractPushPublisher.java:51)
      at jdk.incubator.httpclient/jdk.incubator.http.ResponseContent.pushBodyFixed(ResponseContent.java:277)
      at jdk.incubator.httpclient/jdk.incubator.http.ResponseContent.pushBody(ResponseContent.java:112)
      at jdk.incubator.httpclient/jdk.incubator.http.Http1Response.lambda$readBody$2(Http1Response.java:161)
      at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
      at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
      at java.base/java.lang.Thread.run(Thread.java:844)
      Caused by: jdk.incubator.http.WebSocketHandshakeException
      at jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.OpeningHandshake.resultFrom(OpeningHandshake.java:213)
      at java.base/java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:1072)
      ... 11 more
      Caused by: jdk.incubator.http.internal.websocket.CheckFailedException: Response field 'Sec-WebSocket-Version' present: [13]
      at jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.OpeningHandshake.checkFailed(OpeningHandshake.java:312)
      at jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.OpeningHandshake.requireAbsent(OpeningHandshake.java:281)
      at jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.OpeningHandshake.handleResponse(OpeningHandshake.java:243)
      at jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.OpeningHandshake.resultFrom(OpeningHandshake.java:209)
      ... 12 more

      Given the exception, I use the following code to print information.

                          WebSocketHandshakeException e = (WebSocketHandshakeException) t;
                          HttpResponse<?> res = e.getResponse();
                          if (res != null) {
                              out.println(res.statusCode()+" "+res.version().toString());
                              HttpHeaders hs = res.headers();
                              if (hs != null) {
                                  Util.dumpHeaders(hs.map(), "websocket response");
                              } else {
                                  out.println("response headers is null");
                              }
                              HttpRequest req=res.request();
                              hs=req.headers();
                              if (hs != null) {
                                  Util.dumpHeaders(hs.map(), "websocket request");
                              } else {
                                  out.println("request headers is null");
                              }
                          } else {
                              out.println("response is null");
                          }


      And the following is the information printed:


      101 HTTP_1_1
      start headers for websocket response
      cf-ray: 3bd14275db0e787a-LAX
      connection: upgrade
      date: Mon, 13 Nov 2017 11:06:05 GMT
      sec-websocket-accept: S9VUj2iAxVwQjWz7uzDqi/Z2QKA=
      sec-websocket-protocol: websocket
      sec-websocket-version: 13
      server: cloudflare-nginx
      set-cookie: __cfduid=d9f8e36dffb020ed4b39635b3a9ebf8d41510571165; expires=Tue, 13-Nov-18 11:06:05 GMT; path=/; domain=.bitfinex.com; HttpOnly
      upgrade: websocket
      websocket-server: uWebSockets
      end headers for websocket response

      start headers for websocket request
      Sec-WebSocket-Key: hm2oYUkReBXF6rV1CIdDhA==
      Sec-WebSocket-Protocol: websocket
      Sec-WebSocket-Version: 13
      end headers for websocket request


      By the way, the server is good, if at the client side, I use https://github.com/TooTallNate/Java-WebSocket, then there is no problem in handshake, and connects OK. The handshake headers as follows:

      request:

      GET /ws HTTP/1.1
      Connection: Upgrade
      Host: api.bitfinex.com:443
      Sec-WebSocket-Key: rLc7RM2rrTWxWstuRk1OlA==
      Sec-WebSocket-Version: 13
      Upgrade: websocket


      response:

      HTTP/1.1 101 Switching Protocols
      Date: Mon, 13 Nov 2017 11:01:33 GMT
      Connection: upgrade
      Set-Cookie: __cfduid=db9360a709a6bccbe4d2faeb5f184b17d1510570893; expires=Tue, 13-Nov-18 11:01:33 GMT; path=/; domain=.bitfinex.com; HttpOnly
      Upgrade: websocket
      Sec-WebSocket-Accept: ijGieuFWjnBMGSLimegf5UGlIuQ=
      Sec-WebSocket-Version: 13
      WebSocket-Server: uWebSockets
      Server: cloudflare-nginx
      CF-RAY: 3bd13bd36faa51ac-SJC


      Clearly, the jdk9 websocket the requirement, that the header "Sec-WebSocket-Version" should be absent in the response, caused the exception.



      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      public class WebSocketTest2 implements WebSocket.Listener {

          static String url
                  =//"wss://real.okcoin.cn:10440/websocket/okcoinapi"; //"{'event':'addChannel','channel':'ok_btccny_ticker'}"
                  "wss://api.bitfinex.com/ws";

          WebSocket wsocket;
          static HttpClient client;
          static PrintWriter out;
          static boolean debug;

          public static void setDebug(PrintWriter pw) {
              out = pw;
              debug = out != null;
          }
          static WebSocketTest2 one;

          public static void main(String[] args) {
              try {

                  Util.setDebug(out);
                  one = new WebSocketTest2();
                  one.test();

                  // wait 5 seconds for messages from websocket
                  //Thread.sleep(5000);
              } catch (Throwable ex) {
                  if (debug) {
                      ex.printStackTrace(out);
                      out.flush();
                  }
              }
              if (debug) {
                  out.flush();
              }
          }

          void test() throws URISyntaxException {
              //System.out.println("debug:"+debug+" out:"+out);
              if (debug) {
                  out.println("will try to create a websocket");
                  out.flush();
              }

              client = HttpClient.newHttpClient​();
              WebSocket.Builder builder = client.newWebSocketBuilder(new URI(url), this);
              builder.subprotocols("websocket");
              builder.buildAsync().handle((WebSocket ws, Throwable t) -> {
                  if (t != null) {
                      if (debug) {
                          out.println("websocket creation exception");
                      }
                      t.printStackTrace(out);
                      if(t instanceof CompletionException){
                          t=t.getCause();
                      }
                      if (t instanceof WebSocketHandshakeException) {
                          WebSocketHandshakeException e = (WebSocketHandshakeException) t;
                          HttpResponse<?> res = e.getResponse();
                          if (res != null) {
                              out.println(res.statusCode()+" "+res.version().toString());
                              HttpHeaders hs = res.headers();
                              if (hs != null) {
                                  Util.dumpHeaders(hs.map(), "websocket response");
                              } else {
                                  out.println("response headers is null");
                              }
                              HttpRequest req=res.request();
                              hs=req.headers();
                              if (hs != null) {
                                  Util.dumpHeaders(hs.map(), "websocket request");
                              } else {
                                  out.println("request headers is null");
                              }
                          } else {
                              out.println("response is null");
                          }
                          
                          
                      }
                      out.flush();
                      return null;
                  }
                  if (debug) {
                      out.println("websocket is created");
                      out.flush();
                  }
                  return ws;
              }).thenAccept(ws -> {
                  wsocket = ws;
                  ws.sendText(url, false);
              });
          }

          @Override
          public CompletionStage<?> onText(WebSocket webSocket, CharSequence message, WebSocket.MessagePart part) {
              try {
                  if (debug) {
                      out.println(message);
                  }
                  JsonReader jr = Json.createReader(new StringReader(message.toString()));
                  JsonStructure js = jr.read();
                  JsonValue.ValueType vt = js.getValueType();
                  if (vt == vt.ARRAY) {

                  } else {
                      //jr.readObject()
                  }
              } catch (Throwable t) {

              }
              webSocket.request(1);
              return null;
          }

      }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      use alternative websocket package.

      SUPPORT :
      YES

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              psonal Pallavi Sonal (Inactive)
              Reporter:
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved: