30 import jdk.incubator.http.WebSocket; |
30 import jdk.incubator.http.WebSocket; |
31 import org.testng.annotations.Test; |
31 import org.testng.annotations.Test; |
32 |
32 |
33 import java.io.IOException; |
33 import java.io.IOException; |
34 import java.nio.ByteBuffer; |
34 import java.nio.ByteBuffer; |
|
35 import java.nio.CharBuffer; |
|
36 import java.nio.channels.SocketChannel; |
|
37 import java.nio.charset.StandardCharsets; |
35 import java.util.concurrent.CompletableFuture; |
38 import java.util.concurrent.CompletableFuture; |
36 import java.util.concurrent.CompletionException; |
39 import java.util.concurrent.CompletionException; |
|
40 import java.util.concurrent.TimeUnit; |
|
41 import java.util.concurrent.TimeoutException; |
37 |
42 |
38 import static jdk.incubator.http.HttpClient.newHttpClient; |
43 import static jdk.incubator.http.HttpClient.newHttpClient; |
39 import static jdk.incubator.http.WebSocket.NORMAL_CLOSURE; |
44 import static jdk.incubator.http.WebSocket.NORMAL_CLOSURE; |
40 import static org.testng.Assert.assertThrows; |
45 import static org.testng.Assert.assertThrows; |
41 |
46 |
42 public class Exceptionally { |
47 public class Exceptionally { |
43 |
48 |
44 static final Class<NullPointerException> NPE = NullPointerException.class; |
49 private static final Class<NullPointerException> NPE |
|
50 = NullPointerException.class; |
|
51 private static final Class<IllegalArgumentException> IAE |
|
52 = IllegalArgumentException.class; |
45 |
53 |
46 @Test |
54 @Test |
47 public void testNull() throws IOException { |
55 public void testNull() throws IOException { |
48 try (DummyWebSocketServer server = new DummyWebSocketServer()) { |
56 try (DummyWebSocketServer server = new DummyWebSocketServer()) { |
49 server.open(); |
57 server.open(); |
50 WebSocket ws = newHttpClient() |
58 WebSocket ws = newHttpClient() |
51 .newWebSocketBuilder() |
59 .newWebSocketBuilder() |
52 .buildAsync(server.getURI(), new WebSocket.Listener() { }) |
60 .buildAsync(server.getURI(), new WebSocket.Listener() { }) |
53 .join(); |
61 .join(); |
54 |
62 |
|
63 assertThrows(NPE, () -> ws.sendText(null, false)); |
|
64 assertThrows(NPE, () -> ws.sendText(null, true)); |
|
65 assertThrows(NPE, () -> ws.sendBinary(null, false)); |
|
66 assertThrows(NPE, () -> ws.sendBinary(null, true)); |
55 assertThrows(NPE, () -> ws.sendPing(null)); |
67 assertThrows(NPE, () -> ws.sendPing(null)); |
56 assertThrows(NPE, () -> ws.sendPong(null)); |
68 assertThrows(NPE, () -> ws.sendPong(null)); |
57 assertThrows(NPE, () -> ws.sendClose(NORMAL_CLOSURE, null)); |
69 assertThrows(NPE, () -> ws.sendClose(NORMAL_CLOSURE, null)); |
58 |
70 } |
59 // ... add more NPE scenarios |
|
60 } |
|
61 } |
|
62 |
|
63 private static String stringWithNBytes(int n) { |
|
64 StringBuilder sb = new StringBuilder(n); |
|
65 for (int i = 0; i < n; i++) |
|
66 sb.append("A"); |
|
67 return sb.toString(); |
|
68 } |
71 } |
69 |
72 |
70 @Test |
73 @Test |
71 public void testIllegalArgument() throws IOException { |
74 public void testIllegalArgument() throws IOException { |
72 try (DummyWebSocketServer server = new DummyWebSocketServer()) { |
75 try (DummyWebSocketServer server = new DummyWebSocketServer()) { |
77 .join(); |
80 .join(); |
78 |
81 |
79 assertIAE(ws.sendPing(ByteBuffer.allocate(126))); |
82 assertIAE(ws.sendPing(ByteBuffer.allocate(126))); |
80 assertIAE(ws.sendPing(ByteBuffer.allocate(127))); |
83 assertIAE(ws.sendPing(ByteBuffer.allocate(127))); |
81 assertIAE(ws.sendPing(ByteBuffer.allocate(128))); |
84 assertIAE(ws.sendPing(ByteBuffer.allocate(128))); |
82 assertIAE(ws.sendPing(ByteBuffer.allocate(150))); |
85 assertIAE(ws.sendPing(ByteBuffer.allocate(129))); |
|
86 assertIAE(ws.sendPing(ByteBuffer.allocate(256))); |
83 |
87 |
84 assertIAE(ws.sendPong(ByteBuffer.allocate(126))); |
88 assertIAE(ws.sendPong(ByteBuffer.allocate(126))); |
85 assertIAE(ws.sendPong(ByteBuffer.allocate(127))); |
89 assertIAE(ws.sendPong(ByteBuffer.allocate(127))); |
86 assertIAE(ws.sendPong(ByteBuffer.allocate(128))); |
90 assertIAE(ws.sendPong(ByteBuffer.allocate(128))); |
87 assertIAE(ws.sendPong(ByteBuffer.allocate(150))); |
91 assertIAE(ws.sendPong(ByteBuffer.allocate(129))); |
|
92 assertIAE(ws.sendPong(ByteBuffer.allocate(256))); |
|
93 |
|
94 assertIAE(ws.sendText(incompleteString(), true)); |
|
95 assertIAE(ws.sendText(incompleteString(), false)); |
|
96 assertIAE(ws.sendText(malformedString(), true)); |
|
97 assertIAE(ws.sendText(malformedString(), false)); |
88 |
98 |
89 assertIAE(ws.sendClose(NORMAL_CLOSURE, stringWithNBytes(124))); |
99 assertIAE(ws.sendClose(NORMAL_CLOSURE, stringWithNBytes(124))); |
90 assertIAE(ws.sendClose(NORMAL_CLOSURE, stringWithNBytes(150))); |
100 assertIAE(ws.sendClose(NORMAL_CLOSURE, stringWithNBytes(125))); |
91 assertIAE(ws.sendClose(NORMAL_CLOSURE - 1, "a reason")); |
101 assertIAE(ws.sendClose(NORMAL_CLOSURE, stringWithNBytes(128))); |
92 |
102 assertIAE(ws.sendClose(NORMAL_CLOSURE, stringWithNBytes(256))); |
93 // ... add more CF complete exceptionally scenarios |
103 assertIAE(ws.sendClose(NORMAL_CLOSURE, stringWithNBytes(257))); |
94 } |
104 assertIAE(ws.sendClose(NORMAL_CLOSURE, stringWith2NBytes((123 / 2) + 1))); |
95 } |
105 assertIAE(ws.sendClose(NORMAL_CLOSURE, malformedString())); |
96 |
106 assertIAE(ws.sendClose(NORMAL_CLOSURE, incompleteString())); |
97 @Test |
107 |
98 public void testIllegalState() throws IOException { |
108 assertIAE(ws.sendClose(-2, "a reason")); |
|
109 assertIAE(ws.sendClose(-1, "a reason")); |
|
110 assertIAE(ws.sendClose(0, "a reason")); |
|
111 assertIAE(ws.sendClose(1, "a reason")); |
|
112 assertIAE(ws.sendClose(500, "a reason")); |
|
113 assertIAE(ws.sendClose(998, "a reason")); |
|
114 assertIAE(ws.sendClose(999, "a reason")); |
|
115 assertIAE(ws.sendClose(1002, "a reason")); |
|
116 assertIAE(ws.sendClose(1003, "a reason")); |
|
117 assertIAE(ws.sendClose(1006, "a reason")); |
|
118 assertIAE(ws.sendClose(1007, "a reason")); |
|
119 assertIAE(ws.sendClose(1009, "a reason")); |
|
120 assertIAE(ws.sendClose(1010, "a reason")); |
|
121 assertIAE(ws.sendClose(1012, "a reason")); |
|
122 assertIAE(ws.sendClose(1013, "a reason")); |
|
123 assertIAE(ws.sendClose(1015, "a reason")); |
|
124 assertIAE(ws.sendClose(5000, "a reason")); |
|
125 assertIAE(ws.sendClose(32768, "a reason")); |
|
126 assertIAE(ws.sendClose(65535, "a reason")); |
|
127 assertIAE(ws.sendClose(65536, "a reason")); |
|
128 assertIAE(ws.sendClose(Integer.MAX_VALUE, "a reason")); |
|
129 assertIAE(ws.sendClose(Integer.MIN_VALUE, "a reason")); |
|
130 |
|
131 assertThrows(IAE, () -> ws.request(Integer.MIN_VALUE)); |
|
132 assertThrows(IAE, () -> ws.request(-1)); |
|
133 assertThrows(IAE, () -> ws.request(0)); |
|
134 } |
|
135 } |
|
136 |
|
137 @Test |
|
138 public void testIllegalStateOutstanding1() throws Exception { |
|
139 try (DummyWebSocketServer server = notReadingServer()) { |
|
140 server.open(); |
|
141 WebSocket ws = newHttpClient() |
|
142 .newWebSocketBuilder() |
|
143 .buildAsync(server.getURI(), new WebSocket.Listener() { }) |
|
144 .join(); |
|
145 |
|
146 ByteBuffer data = ByteBuffer.allocate(65536); |
|
147 for (int i = 0; ; i++) { |
|
148 System.out.println("cycle #" + i); |
|
149 try { |
|
150 ws.sendBinary(data, true).get(10, TimeUnit.SECONDS); |
|
151 data.clear(); |
|
152 } catch (TimeoutException e) { |
|
153 break; |
|
154 } |
|
155 } |
|
156 assertISE(ws.sendBinary(ByteBuffer.allocate(0), true)); |
|
157 assertISE(ws.sendText("", true)); |
|
158 } |
|
159 } |
|
160 |
|
161 @Test |
|
162 public void testIllegalStateOutstanding2() throws Exception { |
|
163 try (DummyWebSocketServer server = notReadingServer()) { |
|
164 server.open(); |
|
165 WebSocket ws = newHttpClient() |
|
166 .newWebSocketBuilder() |
|
167 .buildAsync(server.getURI(), new WebSocket.Listener() { }) |
|
168 .join(); |
|
169 |
|
170 CharBuffer data = CharBuffer.allocate(65536); |
|
171 for (int i = 0; ; i++) { |
|
172 System.out.println("cycle #" + i); |
|
173 try { |
|
174 ws.sendText(data, true).get(10, TimeUnit.SECONDS); |
|
175 data.clear(); |
|
176 } catch (TimeoutException e) { |
|
177 break; |
|
178 } |
|
179 } |
|
180 assertISE(ws.sendText("", true)); |
|
181 assertISE(ws.sendBinary(ByteBuffer.allocate(0), true)); |
|
182 } |
|
183 } |
|
184 |
|
185 private static DummyWebSocketServer notReadingServer() { |
|
186 return new DummyWebSocketServer() { |
|
187 @Override |
|
188 protected void serve(SocketChannel channel) throws IOException { |
|
189 try { |
|
190 Thread.sleep(Long.MAX_VALUE); |
|
191 } catch (InterruptedException e) { |
|
192 throw new IOException(e); |
|
193 } |
|
194 } |
|
195 }; |
|
196 } |
|
197 |
|
198 @Test |
|
199 public void testIllegalStateIntermixed1() throws IOException { |
|
200 try (DummyWebSocketServer server = new DummyWebSocketServer()) { |
|
201 server.open(); |
|
202 WebSocket ws = newHttpClient() |
|
203 .newWebSocketBuilder() |
|
204 .buildAsync(server.getURI(), new WebSocket.Listener() { }) |
|
205 .join(); |
|
206 |
|
207 ws.sendBinary(ByteBuffer.allocate(16), false).join(); |
|
208 assertISE(ws.sendText("text", false)); |
|
209 assertISE(ws.sendText("text", true)); |
|
210 } |
|
211 } |
|
212 |
|
213 @Test |
|
214 public void testIllegalStateIntermixed2() throws IOException { |
|
215 try (DummyWebSocketServer server = new DummyWebSocketServer()) { |
|
216 server.open(); |
|
217 WebSocket ws = newHttpClient() |
|
218 .newWebSocketBuilder() |
|
219 .buildAsync(server.getURI(), new WebSocket.Listener() { }) |
|
220 .join(); |
|
221 |
|
222 ws.sendText("text", false).join(); |
|
223 assertISE(ws.sendBinary(ByteBuffer.allocate(16), false)); |
|
224 assertISE(ws.sendBinary(ByteBuffer.allocate(16), true)); |
|
225 } |
|
226 } |
|
227 |
|
228 private static String malformedString() { |
|
229 return new String(new char[]{0xDC00, 0xD800}); |
|
230 } |
|
231 |
|
232 private static String incompleteString() { |
|
233 return new String(new char[]{0xD800}); |
|
234 } |
|
235 |
|
236 private static String stringWithNBytes(int n) { |
|
237 StringBuilder sb = new StringBuilder(n); |
|
238 for (int i = 0; i < n; i++) { |
|
239 sb.append("A"); |
|
240 } |
|
241 return sb.toString(); |
|
242 } |
|
243 |
|
244 private static String stringWith2NBytes(int n) { |
|
245 // Russian alphabet repeated cyclically |
|
246 char FIRST = '\u0410'; |
|
247 char LAST = '\u042F'; |
|
248 StringBuilder sb = new StringBuilder(n); |
|
249 char c = FIRST; |
|
250 for (int i = 0; i < n; i++) { |
|
251 if (++c > LAST) { |
|
252 c = FIRST; |
|
253 } |
|
254 sb.append(c); |
|
255 } |
|
256 String s = sb.toString(); |
|
257 assert s.length() == n && s.getBytes(StandardCharsets.UTF_8).length == 2 * n; |
|
258 return s; |
|
259 } |
|
260 |
|
261 @Test |
|
262 public void testIllegalStateSendClose() throws IOException { |
99 try (DummyWebSocketServer server = new DummyWebSocketServer()) { |
263 try (DummyWebSocketServer server = new DummyWebSocketServer()) { |
100 server.open(); |
264 server.open(); |
101 WebSocket ws = newHttpClient() |
265 WebSocket ws = newHttpClient() |
102 .newWebSocketBuilder() |
266 .newWebSocketBuilder() |
103 .buildAsync(server.getURI(), new WebSocket.Listener() { }) |
267 .buildAsync(server.getURI(), new WebSocket.Listener() { }) |
104 .join(); |
268 .join(); |
105 |
269 |
106 ws.sendClose(NORMAL_CLOSURE, "normal close").join(); |
270 ws.sendClose(NORMAL_CLOSURE, "normal close").join(); |
|
271 |
|
272 assertISE(ws.sendText("", true)); |
|
273 assertISE(ws.sendText("", false)); |
|
274 assertISE(ws.sendText("abc", true)); |
|
275 assertISE(ws.sendText("abc", false)); |
|
276 assertISE(ws.sendBinary(ByteBuffer.allocate(0), true)); |
|
277 assertISE(ws.sendBinary(ByteBuffer.allocate(0), false)); |
|
278 assertISE(ws.sendBinary(ByteBuffer.allocate(1), true)); |
|
279 assertISE(ws.sendBinary(ByteBuffer.allocate(1), false)); |
107 |
280 |
108 assertISE(ws.sendPing(ByteBuffer.allocate(125))); |
281 assertISE(ws.sendPing(ByteBuffer.allocate(125))); |
109 assertISE(ws.sendPing(ByteBuffer.allocate(124))); |
282 assertISE(ws.sendPing(ByteBuffer.allocate(124))); |
110 assertISE(ws.sendPing(ByteBuffer.allocate( 1))); |
283 assertISE(ws.sendPing(ByteBuffer.allocate(1))); |
111 assertISE(ws.sendPing(ByteBuffer.allocate( 0))); |
284 assertISE(ws.sendPing(ByteBuffer.allocate(0))); |
112 |
285 |
113 assertISE(ws.sendPong(ByteBuffer.allocate(125))); |
286 assertISE(ws.sendPong(ByteBuffer.allocate(125))); |
114 assertISE(ws.sendPong(ByteBuffer.allocate(124))); |
287 assertISE(ws.sendPong(ByteBuffer.allocate(124))); |
115 assertISE(ws.sendPong(ByteBuffer.allocate( 1))); |
288 assertISE(ws.sendPong(ByteBuffer.allocate(1))); |
116 assertISE(ws.sendPong(ByteBuffer.allocate( 0))); |
289 assertISE(ws.sendPong(ByteBuffer.allocate(0))); |
117 |
290 } |
118 // ... add more CF complete exceptionally scenarios |
291 } |
119 } |
292 |
120 } |
293 private static void assertIAE(CompletableFuture<?> stage) { |
121 |
294 assertExceptionally(IAE, stage); |
122 private void assertIAE(CompletableFuture<?> stage) { |
295 } |
123 assertExceptionally(IllegalArgumentException.class, stage); |
296 |
124 } |
297 private static void assertISE(CompletableFuture<?> stage) { |
125 |
|
126 private void assertISE(CompletableFuture<?> stage) { |
|
127 assertExceptionally(IllegalStateException.class, stage); |
298 assertExceptionally(IllegalStateException.class, stage); |
128 } |
299 } |
129 |
300 |
130 private void assertExceptionally(Class<? extends Throwable> clazz, |
301 private static void assertExceptionally(Class<? extends Throwable> clazz, |
131 CompletableFuture<?> stage) { |
302 CompletableFuture<?> stage) { |
132 stage.handle((result, error) -> { |
303 stage.handle((result, error) -> { |
133 if (error instanceof CompletionException) { |
304 if (error instanceof CompletionException) { |
134 Throwable cause = error.getCause(); |
305 Throwable cause = error.getCause(); |
135 if (cause == null) { |
306 if (cause == null) { |
136 throw new AssertionError("Unexpected null cause: " + error); |
307 throw new AssertionError("Unexpected null cause: " + error); |