http-client-branch: (WebSocket) relaxed Opening Handshake constraints; originated from JDK-8191646 http-client-branch
authorprappo
Wed, 06 Dec 2017 18:47:54 +0300
branchhttp-client-branch
changeset 55969 1da220f80a5d
parent 55968 11a97b370db0
child 55970 261d4d2f77e2
http-client-branch: (WebSocket) relaxed Opening Handshake constraints; originated from JDK-8191646
src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/OpeningHandshake.java
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/OpeningHandshake.java	Wed Dec 06 14:29:06 2017 +0000
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/OpeningHandshake.java	Wed Dec 06 18:47:54 2017 +0300
@@ -25,7 +25,16 @@
 
 package jdk.incubator.http.internal.websocket;
 
+import jdk.incubator.http.HttpClient;
+import jdk.incubator.http.HttpClient.Version;
+import jdk.incubator.http.HttpHeaders;
+import jdk.incubator.http.HttpRequest;
+import jdk.incubator.http.HttpResponse;
+import jdk.incubator.http.HttpResponse.BodyHandler;
+import jdk.incubator.http.WebSocketHandshakeException;
 import jdk.incubator.http.internal.common.MinimalFuture;
+import jdk.incubator.http.internal.common.Pair;
+import jdk.incubator.http.internal.common.Utils;
 
 import java.io.IOException;
 import java.net.InetSocketAddress;
@@ -33,16 +42,6 @@
 import java.net.ProxySelector;
 import java.net.URI;
 import java.net.URISyntaxException;
-import jdk.incubator.http.HttpClient;
-import jdk.incubator.http.HttpClient.Version;
-import jdk.incubator.http.HttpHeaders;
-import jdk.incubator.http.HttpRequest;
-import jdk.incubator.http.HttpResponse;
-import jdk.incubator.http.HttpResponse.BodyHandler;
-import jdk.incubator.http.WebSocketHandshakeException;
-import jdk.incubator.http.internal.common.Pair;
-import jdk.incubator.http.internal.common.Utils;
-
 import java.net.URLPermission;
 import java.nio.charset.StandardCharsets;
 import java.security.AccessController;
@@ -77,6 +76,7 @@
     private static final String HEADER_KEY        = "Sec-WebSocket-Key";
     private static final String HEADER_PROTOCOL   = "Sec-WebSocket-Protocol";
     private static final String HEADER_VERSION    = "Sec-WebSocket-Version";
+    private static final String VERSION           = "13";  // WebSocket's lucky number
 
     private static final Set<String> ILLEGAL_HEADERS;
 
@@ -130,7 +130,7 @@
             String p = this.subprotocols.stream().collect(Collectors.joining(", "));
             requestBuilder.header(HEADER_PROTOCOL, p);
         }
-        requestBuilder.header(HEADER_VERSION, "13"); // WebSocket's lucky number
+        requestBuilder.header(HEADER_VERSION, VERSION);
         this.nonce = createNonce();
         requestBuilder.header(HEADER_KEY, this.nonce);
         // Setting request version to HTTP/1.1 forcibly, since it's not possible
@@ -249,7 +249,10 @@
         if (!connection.equalsIgnoreCase("Upgrade")) {
             throw checkFailed("Bad response field: " + HEADER_CONNECTION);
         }
-        requireAbsent(headers, HEADER_VERSION);
+        Optional<String> version = requireAtMostOne(headers, HEADER_VERSION);
+        if (version.isPresent() && !version.get().equals(VERSION)) {
+            throw checkFailed("Bad response field: " + HEADER_VERSION);
+        }
         requireAbsent(headers, HEADER_EXTENSIONS);
         String x = this.nonce + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
         this.sha1.update(x.getBytes(StandardCharsets.ISO_8859_1));
@@ -293,6 +296,18 @@
         }
     }
 
+    private static Optional<String> requireAtMostOne(HttpHeaders responseHeaders,
+                                                     String headerName)
+    {
+        List<String> values = responseHeaders.allValues(headerName);
+        if (values.size() > 1) {
+            throw checkFailed(format("Response field '%s' multivalued: %s",
+                                     headerName,
+                                     stringOf(values)));
+        }
+        return values.stream().findFirst();
+    }
+
     private static String requireSingle(HttpHeaders responseHeaders,
                                         String headerName)
     {