1 /*
   2  * Copyright (c) 2016, 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 
  38 import static jdk.incubator.http.internal.websocket.TestSupport.assertCompletesExceptionally;
  39 import static jdk.incubator.http.internal.websocket.TestSupport.assertThrows;
  40 
  41 /*
  42  * In some places in this class a new String is created out of a string literal.
  43  * The idea is to make sure the code under test relies on something better than
  44  * the reference equality ( == ) for string equality checks.
  45  */
  46 public class BuildingWebSocketTest {
  47 
  48     @Test
  49     public void nulls() {
  50         HttpClient c = HttpClient.newHttpClient();
  51         URI uri = URI.create("ws://websocket.example.com");
  52 
  53         assertThrows(NullPointerException.class,
  54                      () -> c.newWebSocketBuilder(null, listener()));
  55         assertThrows(NullPointerException.class,
  56                      () -> c.newWebSocketBuilder(uri, null));
  57         assertThrows(NullPointerException.class,
  58                      () -> c.newWebSocketBuilder(null, null));
  59         assertThrows(NullPointerException.class,
  60                      () -> c.newWebSocketBuilder(uri, listener())
  61                             .header(null, "value"));
  62         assertThrows(NullPointerException.class,
  63                      () -> c.newWebSocketBuilder(uri, listener())
  64                             .header("name", null));
  65         assertThrows(NullPointerException.class,
  66                      () -> c.newWebSocketBuilder(uri, listener())
  67                             .header(null, null));
  68         assertThrows(NullPointerException.class,
  69                      () -> c.newWebSocketBuilder(uri, listener())
  70                             .subprotocols(null));
  71         assertThrows(NullPointerException.class,
  72                      () -> c.newWebSocketBuilder(uri, listener())
  73                             .subprotocols(null, "sub1"));
  74         assertThrows(NullPointerException.class,
  75                      () -> c.newWebSocketBuilder(uri, listener())
  76                             .subprotocols("sub1.example.com", (String) null));
  77         assertThrows(NullPointerException.class,
  78                      () -> c.newWebSocketBuilder(uri, listener())
  79                             .subprotocols("sub1.example.com", (String[]) null));
  80         assertThrows(NullPointerException.class,
  81                      () -> c.newWebSocketBuilder(uri, listener())
  82                             .subprotocols("sub1.example.com",
  83                                           "sub2.example.com",
  84                                           null));
  85         assertThrows(NullPointerException.class,
  86                      () -> c.newWebSocketBuilder(uri, listener())
  87                             .connectTimeout(null));
  88     }
  89 
  90     @Test(dataProvider = "badURIs")
  91     void illegalURI(String u) {
  92         WebSocket.Builder b = HttpClient.newHttpClient()
  93                 .newWebSocketBuilder(URI.create(u), listener());
  94         assertCompletesExceptionally(IllegalArgumentException.class, b.buildAsync());
  95     }
  96 
  97     @Test
  98     public void illegalHeaders() {
  99         List<String> headers = List.of("Authorization",
 100                                        "Connection",
 101                                        "Cookie",
 102                                        "Content-Length",
 103                                        "Date",
 104                                        "Expect",
 105                                        "From",
 106                                        "Host",
 107                                        "Origin",
 108                                        "Proxy-Authorization",
 109                                        "Referer",
 110                                        "User-agent",
 111                                        "Upgrade",
 112                                        "Via",
 113                                        "Warning",
 114                                        "Sec-WebSocket-Accept",
 115                                        "Sec-WebSocket-Extensions",
 116                                        "Sec-WebSocket-Key",
 117                                        "Sec-WebSocket-Protocol",
 118                                        "Sec-WebSocket-Version").stream()
 119                 .map(String::new).collect(Collectors.toList());
 120 
 121         Function<String, CompletionStage<?>> f =
 122                 header -> HttpClient
 123                         .newHttpClient()
 124                         .newWebSocketBuilder(URI.create("ws://websocket.example.com"),
 125                                              listener())
 126                         .buildAsync();
 127 
 128         headers.forEach(h -> assertCompletesExceptionally(IllegalArgumentException.class, f.apply(h)));
 129     }
 130 
 131     // TODO: test for bad syntax headers
 132     // TODO: test for overwrites (subprotocols) and additions (headers)
 133 
 134     @Test(dataProvider = "badSubprotocols")
 135     public void illegalSubprotocolsSyntax(String s) {
 136         WebSocket.Builder b = HttpClient.newHttpClient()
 137                 .newWebSocketBuilder(URI.create("ws://websocket.example.com"),
 138                                      listener());
 139         b.subprotocols(s);
 140         assertCompletesExceptionally(IllegalArgumentException.class, b.buildAsync());
 141     }
 142 
 143     @Test(dataProvider = "duplicatingSubprotocols")
 144     public void illegalSubprotocolsDuplicates(String mostPreferred,
 145                                               String[] lesserPreferred) {
 146         WebSocket.Builder b = HttpClient.newHttpClient()
 147                 .newWebSocketBuilder(URI.create("ws://websocket.example.com"),
 148                                      listener());
 149         b.subprotocols(mostPreferred, lesserPreferred);
 150         assertCompletesExceptionally(IllegalArgumentException.class, b.buildAsync());
 151     }
 152 
 153     @Test(dataProvider = "badConnectTimeouts")
 154     public void illegalConnectTimeout(Duration d) {
 155         WebSocket.Builder b = HttpClient.newHttpClient()
 156                 .newWebSocketBuilder(URI.create("ws://websocket.example.com"),
 157                                      listener());
 158         b.connectTimeout(d);
 159         assertCompletesExceptionally(IllegalArgumentException.class, b.buildAsync());
 160     }
 161 
 162     @DataProvider
 163     public Object[][] badURIs() {
 164         return new Object[][]{
 165                 {"http://example.com"},
 166                 {"ftp://example.com"},
 167                 {"wss://websocket.example.com/hello#fragment"},
 168                 {"ws://websocket.example.com/hello#fragment"},
 169         };
 170     }
 171 
 172     @DataProvider
 173     public Object[][] badConnectTimeouts() {
 174         return new Object[][]{
 175                 {Duration.ofDays   ( 0)},
 176                 {Duration.ofDays   (-1)},
 177                 {Duration.ofHours  ( 0)},
 178                 {Duration.ofHours  (-1)},
 179                 {Duration.ofMinutes( 0)},
 180                 {Duration.ofMinutes(-1)},
 181                 {Duration.ofSeconds( 0)},
 182                 {Duration.ofSeconds(-1)},
 183                 {Duration.ofMillis ( 0)},
 184                 {Duration.ofMillis (-1)},
 185                 {Duration.ofNanos  ( 0)},
 186                 {Duration.ofNanos  (-1)},
 187                 {Duration.ZERO},
 188         };
 189     }
 190 
 191     // https://tools.ietf.org/html/rfc7230#section-3.2.6
 192     // https://tools.ietf.org/html/rfc20
 193     @DataProvider
 194     public static Object[][] badSubprotocols() {
 195         return new Object[][]{
 196                 {new String("")},
 197                 {"round-brackets("},
 198                 {"round-brackets)"},
 199                 {"comma,"},
 200                 {"slash/"},
 201                 {"colon:"},
 202                 {"semicolon;"},
 203                 {"angle-brackets<"},
 204                 {"angle-brackets>"},
 205                 {"equals="},
 206                 {"question-mark?"},
 207                 {"at@"},
 208                 {"brackets["},
 209                 {"backslash\\"},
 210                 {"brackets]"},
 211                 {"curly-brackets{"},
 212                 {"curly-brackets}"},
 213                 {"space "},
 214                 {"non-printable-character " + Character.toString((char) 31)},
 215                 {"non-printable-character " + Character.toString((char) 127)},
 216         };
 217     }
 218 
 219     @DataProvider
 220     public static Object[][] duplicatingSubprotocols() {
 221         return new Object[][]{
 222                 {"a.b.c", new String[]{"a.b.c"}},
 223                 {"a.b.c", new String[]{"x.y.z", "p.q.r", "x.y.z"}},
 224                 {"a.b.c", new String[]{new String("a.b.c")}},
 225         };
 226     }
 227 
 228     private static WebSocket.Listener listener() {
 229         return new WebSocket.Listener() { };
 230     }
 231 }