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.Test; 27 import jdk.incubator.http.internal.websocket.Frame.Opcode; 28 29 import java.nio.ByteBuffer; 30 import java.util.Optional; 31 import java.util.OptionalInt; 32 import java.util.OptionalLong; 33 import java.util.function.IntPredicate; 34 import java.util.function.IntUnaryOperator; 35 36 import static java.util.OptionalInt.empty; 37 import static java.util.OptionalInt.of; 38 import static org.testng.Assert.assertEquals; 39 import static jdk.incubator.http.internal.websocket.TestSupport.assertThrows; 40 import static jdk.incubator.http.internal.websocket.TestSupport.forEachBufferPartition; 41 42 public class ReaderTest { 43 44 private long cases, frames; 45 46 @Test 47 void notMinimalEncoding01() { 48 ByteBuffer h = ByteBuffer.allocate(Frame.MAX_HEADER_SIZE_BYTES); 49 h.put((byte) 0b1000_0000).put((byte) 0b0111_1110).putChar((char) 125).flip(); 50 assertThrows(FailWebSocketException.class, 51 ".*(?i)minimally-encoded.*", 52 () -> new Frame.Reader().readFrame(h, new MockConsumer())); 53 } 54 55 @Test 56 void notMinimalEncoding02() { 57 ByteBuffer h = ByteBuffer.allocate(Frame.MAX_HEADER_SIZE_BYTES); 58 h.put((byte) 0b1000_0000).put((byte) 0b0111_1111).putLong(125).flip(); 59 assertThrows(FailWebSocketException.class, 60 ".*(?i)minimally-encoded.*", 61 () -> new Frame.Reader().readFrame(h, new MockConsumer())); 62 } 63 64 @Test 65 void notMinimalEncoding03() { 66 ByteBuffer h = ByteBuffer.allocate(Frame.MAX_HEADER_SIZE_BYTES); 67 h.put((byte) 0b1000_0000).put((byte) 0b0111_1111).putLong(65535).flip(); 68 assertThrows(FailWebSocketException.class, 69 ".*(?i)minimally-encoded.*", 70 () -> new Frame.Reader().readFrame(h, new MockConsumer())); 71 } 72 73 @Test 74 public void negativePayload() { 75 ByteBuffer h = ByteBuffer.allocate(Frame.MAX_HEADER_SIZE_BYTES); 76 h.put((byte) 0b1000_0000).put((byte) 0b0111_1111).putLong(-2L).flip(); 77 assertThrows(FailWebSocketException.class, 78 ".*(?i)negative.*", 79 () -> new Frame.Reader().readFrame(h, new MockConsumer())); 80 } 81 82 @Test 83 public void frameStart() { 84 final long[] payloads = {0, 126, 65536, Integer.MAX_VALUE + 1L}; 85 final OptionalInt[] masks = {empty(), of(-1), of(0), of(0xCAFEBABE), 86 of(Integer.MAX_VALUE), of(Integer.MIN_VALUE)}; 87 for (boolean fin : new boolean[]{true, false}) { 88 for (boolean rsv1 : new boolean[]{true, false}) { 89 for (boolean rsv2 : new boolean[]{true, false}) { 90 for (boolean rsv3 : new boolean[]{true, false}) { 91 for (Opcode opcode : Opcode.values()) { 92 for (long payloadLen : payloads) { 93 for (OptionalInt mask : masks) { 94 verifyFrameStart(fin, rsv1, rsv2, rsv3, opcode, payloadLen, mask); 95 } 96 } 97 } 98 } 99 } 100 } 101 } 102 System.out.println("Frames: " + frames + ", Total cases: " + cases); 103 } 104 105 /* 106 * Tests whether or not the frame starts properly. 107 * That is, a header and the first invocation of payloadData (if any). 108 */ 109 private void verifyFrameStart(boolean fin, 110 boolean rsv1, 111 boolean rsv2, 112 boolean rsv3, 113 Opcode opcode, 114 long payloadLen, 115 OptionalInt mask) { 116 frames++; 117 Frame.HeaderWriter w = new Frame.HeaderWriter(); 118 ByteBuffer h = ByteBuffer.allocate(Frame.MAX_HEADER_SIZE_BYTES); 119 w.fin(fin).rsv1(rsv1).rsv2(rsv2).rsv3(rsv3).opcode(opcode).payloadLen(payloadLen); 120 mask.ifPresentOrElse(w::mask, w::noMask); 121 w.write(h); 122 h.flip(); 123 forEachBufferPartition(h, 124 buffers -> { 125 cases++; 126 Frame.Reader r = new Frame.Reader(); 127 MockConsumer c = new MockConsumer(); 128 for (ByteBuffer b : buffers) { 129 r.readFrame(b, c); 130 } 131 assertEquals(fin, c.fin()); 132 assertEquals(rsv1, c.rsv1()); 133 assertEquals(rsv2, c.rsv2()); 134 assertEquals(rsv3, c.rsv3()); 135 assertEquals(opcode, c.opcode()); 136 assertEquals(mask.isPresent(), c.mask()); 137 assertEquals(payloadLen, c.payloadLen()); 138 assertEquals(mask, c.maskingKey()); 139 assertEquals(payloadLen == 0, c.isEndFrame()); 140 }); 141 } 142 143 /* 144 * Used to verify the order, the number of invocations as well as the 145 * arguments of each individual invocation to Frame.Consumer's methods. 146 */ 147 private static class MockConsumer implements Frame.Consumer { 148 149 private int invocationOrder; 150 151 private Optional<Boolean> fin = Optional.empty(); 152 private Optional<Boolean> rsv1 = Optional.empty(); 153 private Optional<Boolean> rsv2 = Optional.empty(); 154 private Optional<Boolean> rsv3 = Optional.empty(); 155 private Optional<Opcode> opcode = Optional.empty(); 156 private Optional<Boolean> mask = Optional.empty(); 157 private OptionalLong payloadLen = OptionalLong.empty(); 158 private OptionalInt maskingKey = OptionalInt.empty(); 159 160 @Override 161 public void fin(boolean value) { 162 checkAndSetOrder(0, 1); 163 fin = Optional.of(value); 164 } 165 166 @Override 167 public void rsv1(boolean value) { 168 checkAndSetOrder(1, 2); 169 rsv1 = Optional.of(value); 170 } 171 172 @Override 173 public void rsv2(boolean value) { 174 checkAndSetOrder(2, 3); 175 rsv2 = Optional.of(value); 176 } 177 178 @Override 179 public void rsv3(boolean value) { 180 checkAndSetOrder(3, 4); 181 rsv3 = Optional.of(value); 182 } 183 184 @Override 185 public void opcode(Opcode value) { 186 checkAndSetOrder(4, 5); 187 opcode = Optional.of(value); 188 } 189 190 @Override 191 public void mask(boolean value) { 192 checkAndSetOrder(5, 6); 193 mask = Optional.of(value); 194 } 195 196 @Override 197 public void payloadLen(long value) { 198 checkAndSetOrder(p -> p == 5 || p == 6, n -> 7); 199 payloadLen = OptionalLong.of(value); 200 } 201 202 @Override 203 public void maskingKey(int value) { 204 checkAndSetOrder(7, 8); 205 maskingKey = of(value); 206 } 207 208 @Override 209 public void payloadData(ByteBuffer data) { 210 checkAndSetOrder(p -> p == 7 || p == 8, n -> 9); 211 assert payloadLen.isPresent(); 212 if (payloadLen.getAsLong() != 0 && !data.hasRemaining()) { 213 throw new TestSupport.AssertionFailedException("Artefact of reading"); 214 } 215 } 216 217 @Override 218 public void endFrame() { 219 checkAndSetOrder(9, 10); 220 } 221 222 boolean isEndFrame() { 223 return invocationOrder == 10; 224 } 225 226 public boolean fin() { 227 return fin.get(); 228 } 229 230 public boolean rsv1() { 231 return rsv1.get(); 232 } 233 234 public boolean rsv2() { 235 return rsv2.get(); 236 } 237 238 public boolean rsv3() { 239 return rsv3.get(); 240 } 241 242 public Opcode opcode() { 243 return opcode.get(); 244 } 245 246 public boolean mask() { 247 return mask.get(); 248 } 249 250 public long payloadLen() { 251 return payloadLen.getAsLong(); 252 } 253 254 public OptionalInt maskingKey() { 255 return maskingKey; 256 } 257 258 private void checkAndSetOrder(int expectedValue, int newValue) { 259 checkAndSetOrder(p -> p == expectedValue, n -> newValue); 260 } 261 262 private void checkAndSetOrder(IntPredicate expectedValue, 263 IntUnaryOperator newValue) { 264 if (!expectedValue.test(invocationOrder)) { 265 throw new TestSupport.AssertionFailedException( 266 expectedValue + " -> " + newValue); 267 } 268 invocationOrder = newValue.applyAsInt(invocationOrder); 269 } 270 } 271 }