1 /*
   2  * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 package jdk.incubator.http.internal.websocket;
  25 
  26 import org.testng.annotations.DataProvider;
  27 import org.testng.annotations.Test;
  28 
  29 import java.net.URI;
  30 import jdk.incubator.http.HttpClient;
  31 import jdk.incubator.http.WebSocket;
  32 import java.time.Duration;
  33 import java.util.List;
  34 import java.util.concurrent.CompletionStage;
  35 import java.util.function.Function;
  36 import java.util.stream.Collectors;
  37 import java.util.stream.Stream;
  38 
  39 import static jdk.incubator.http.internal.websocket.TestSupport.assertCompletesExceptionally;
  40 import static jdk.incubator.http.internal.websocket.TestSupport.assertThrows;
  41 
  42 /*
  43  * In some places in this class a new String is created out of a string literal.
  44  * The idea is to make sure the code under test relies on something better than
  45  * the reference equality ( == ) for string equality checks.
  46  */
  47 public class BuildingWebSocketTest {
  48 
  49     private final static URI VALID_URI = URI.create("ws://websocket.example.com");
  50 
  51     @Test
  52     public void nullArguments() {
  53         HttpClient c = HttpClient.newHttpClient();
  54 
  55         assertThrows(NullPointerException.class,
  56                      () -> c.newWebSocketBuilder()
  57                             .buildAsync(null, listener()));
  58         assertThrows(NullPointerException.class,
  59                      () -> c.newWebSocketBuilder()
  60                             .buildAsync(VALID_URI, null));
  61         assertThrows(NullPointerException.class,
  62                      () -> c.newWebSocketBuilder()
  63                             .buildAsync(null, null));
  64         assertThrows(NullPointerException.class,
  65                      () -> c.newWebSocketBuilder()
  66                             .header(null, "value"));
  67         assertThrows(NullPointerException.class,
  68                      () -> c.newWebSocketBuilder()
  69                             .header("name", null));
  70         assertThrows(NullPointerException.class,
  71                      () -> c.newWebSocketBuilder()
  72                             .header(null, null));
  73         assertThrows(NullPointerException.class,
  74                      () -> c.newWebSocketBuilder()
  75                             .subprotocols(null));
  76         assertThrows(NullPointerException.class,
  77                      () -> c.newWebSocketBuilder()
  78                             .subprotocols(null, "sub2.example.com"));
  79         assertThrows(NullPointerException.class,
  80                      () -> c.newWebSocketBuilder()
  81                             .subprotocols("sub1.example.com", (String) null));
  82         assertThrows(NullPointerException.class,
  83                      () -> c.newWebSocketBuilder()
  84                             .subprotocols("sub1.example.com", (String[]) null));
  85         assertThrows(NullPointerException.class,
  86                      () -> c.newWebSocketBuilder()
  87                             .subprotocols("sub1.example.com", "sub2.example.com", null));
  88         assertThrows(NullPointerException.class,
  89                      () -> c.newWebSocketBuilder()
  90                              .subprotocols("sub1.example.com", null, "sub3.example.com"));
  91         assertThrows(NullPointerException.class,
  92                      () -> c.newWebSocketBuilder()
  93                             .connectTimeout(null));
  94     }
  95 
  96     @Test(dataProvider = "badURIs")
  97     void illegalURI(URI uri) {
  98         WebSocket.Builder b = HttpClient.newHttpClient().newWebSocketBuilder();
  99         assertCompletesExceptionally(IllegalArgumentException.class,
 100                                      b.buildAsync(uri, listener()));
 101     }
 102 
 103     @Test
 104     public void illegalHeaders() {
 105         List<String> headers =
 106                 List.of("Sec-WebSocket-Accept",
 107                         "Sec-WebSocket-Extensions",
 108                         "Sec-WebSocket-Key",
 109                         "Sec-WebSocket-Protocol",
 110                         "Sec-WebSocket-Version")
 111                         .stream()
 112                         .flatMap(s -> Stream.of(s, new String(s))) // a string and a copy of it
 113                         .collect(Collectors.toList());
 114 
 115         Function<String, CompletionStage<?>> f =
 116                 header -> HttpClient.newHttpClient()
 117                         .newWebSocketBuilder()
 118                         .header(header, "value")
 119                         .buildAsync(VALID_URI, listener());
 120 
 121         headers.forEach(h -> assertCompletesExceptionally(IllegalArgumentException.class, f.apply(h)));
 122     }
 123 
 124     // TODO: test for bad syntax headers
 125     // TODO: test for overwrites (subprotocols) and additions (headers)
 126 
 127     @Test(dataProvider = "badSubprotocols")
 128     public void illegalSubprotocolsSyntax(String s) {
 129         WebSocket.Builder b = HttpClient.newHttpClient()
 130                 .newWebSocketBuilder()
 131                 .subprotocols(s);
 132         assertCompletesExceptionally(IllegalArgumentException.class,
 133                                      b.buildAsync(VALID_URI, listener()));
 134     }
 135 
 136     @Test(dataProvider = "duplicatingSubprotocols")
 137     public void illegalSubprotocolsDuplicates(String mostPreferred,
 138                                               String[] lesserPreferred) {
 139         WebSocket.Builder b = HttpClient.newHttpClient()
 140                 .newWebSocketBuilder()
 141                 .subprotocols(mostPreferred, lesserPreferred);
 142         assertCompletesExceptionally(IllegalArgumentException.class,
 143                                      b.buildAsync(VALID_URI, listener()));
 144     }
 145 
 146     @Test(dataProvider = "badConnectTimeouts")
 147     public void illegalConnectTimeout(Duration d) {
 148         WebSocket.Builder b = HttpClient.newHttpClient()
 149                 .newWebSocketBuilder()
 150                 .connectTimeout(d);
 151         assertCompletesExceptionally(IllegalArgumentException.class,
 152                                      b.buildAsync(VALID_URI, listener()));
 153     }
 154 
 155     @DataProvider
 156     public Object[][] badURIs() {
 157         return new Object[][]{
 158                 {URI.create("http://example.com")},
 159                 {URI.create("ftp://example.com")},
 160                 {URI.create("wss://websocket.example.com/hello#fragment")},
 161                 {URI.create("ws://websocket.example.com/hello#fragment")},
 162         };
 163     }
 164 
 165     @DataProvider
 166     public Object[][] badConnectTimeouts() {
 167         return new Object[][]{
 168                 {Duration.ofDays   ( 0)},
 169                 {Duration.ofDays   (-1)},
 170                 {Duration.ofHours  ( 0)},
 171                 {Duration.ofHours  (-1)},
 172                 {Duration.ofMinutes( 0)},
 173                 {Duration.ofMinutes(-1)},
 174                 {Duration.ofSeconds( 0)},
 175                 {Duration.ofSeconds(-1)},
 176                 {Duration.ofMillis ( 0)},
 177                 {Duration.ofMillis (-1)},
 178                 {Duration.ofNanos  ( 0)},
 179                 {Duration.ofNanos  (-1)},
 180                 {Duration.ZERO},
 181         };
 182     }
 183 
 184     // https://tools.ietf.org/html/rfc7230#section-3.2.6
 185     // https://tools.ietf.org/html/rfc20
 186     @DataProvider
 187     public static Object[][] badSubprotocols() {
 188         return new Object[][]{
 189                 {""},
 190                 {new String("")},
 191                 {"round-brackets("},
 192                 {"round-brackets)"},
 193                 {"comma,"},
 194                 {"slash/"},
 195                 {"colon:"},
 196                 {"semicolon;"},
 197                 {"angle-brackets<"},
 198                 {"angle-brackets>"},
 199                 {"equals="},
 200                 {"question-mark?"},
 201                 {"at@"},
 202                 {"brackets["},
 203                 {"backslash\\"},
 204                 {"brackets]"},
 205                 {"curly-brackets{"},
 206                 {"curly-brackets}"},
 207                 {"space "},
 208                 {"non-printable-character " + Character.toString((char) 31)},
 209                 {"non-printable-character " + Character.toString((char) 127)},
 210         };
 211     }
 212 
 213     @DataProvider
 214     public static Object[][] duplicatingSubprotocols() {
 215         return new Object[][]{
 216                 {"a.b.c", new String[]{"a.b.c"}},
 217                 {"a.b.c", new String[]{"x.y.z", "p.q.r", "x.y.z"}},
 218                 {"a.b.c", new String[]{new String("a.b.c")}},
 219         };
 220     }
 221 
 222     private static WebSocket.Listener listener() {
 223         return new WebSocket.Listener() { };
 224     }
 225 }