1 module hunt.http.codec.websocket.frame.AbstractWebSocketFrame; 2 3 import hunt.http.WebSocketFrame; 4 5 import hunt.collection; 6 import hunt.Exceptions; 7 import hunt.text.Common; 8 import hunt.util.StringBuilder; 9 10 import std.conv; 11 import std.traits; 12 13 /** 14 * A Base Frame as seen in <a href="https://tools.ietf.org/html/rfc6455#section-5.2">RFC 6455. Sec 5.2</a> 15 * <p> 16 * <pre> 17 * 0 1 2 3 18 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 19 * +-+-+-+-+-------+-+-------------+-------------------------------+ 20 * |F|R|R|R| opcode|M| Payload len | Extended payload length | 21 * |I|S|S|S| (4) |A| (7) | (16/64) | 22 * |N|V|V|V| |S| | (if payload len==126/127) | 23 * | |1|2|3| |K| | | 24 * +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + 25 * | Extended payload length continued, if payload len == 127 | 26 * + - - - - - - - - - - - - - - - +-------------------------------+ 27 * | |Masking-key, if MASK set to 1 | 28 * +-------------------------------+-------------------------------+ 29 * | Masking-key (continued) | Payload Data | 30 * +-------------------------------- - - - - - - - - - - - - - - - + 31 * : Payload Data continued ... : 32 * + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 33 * | Payload Data continued ... | 34 * +---------------------------------------------------------------+ 35 * </pre> 36 */ 37 abstract class AbstractWebSocketFrame : WebSocketFrame { 38 39 /** 40 * Combined FIN + RSV1 + RSV2 + RSV3 + OpCode byte. 41 * <p> 42 * <pre> 43 * 1000_0000 (0x80) = fin 44 * 0100_0000 (0x40) = rsv1 45 * 0010_0000 (0x20) = rsv2 46 * 0001_0000 (0x10) = rsv3 47 * 0000_1111 (0x0F) = opcode 48 * </pre> 49 */ 50 protected byte finRsvOp; 51 protected bool masked = false; 52 53 protected byte[] mask; 54 /** 55 * The payload data. 56 * <p> 57 * It is assumed to always be in FLUSH mode (ready to read) in this object. 58 */ 59 protected ByteBuffer data; 60 61 /** 62 * Construct form opcode 63 * 64 * @param opcode the opcode the frame is based on 65 */ 66 protected this(byte opcode) { 67 reset(); 68 setOpCode(opcode); 69 } 70 71 abstract void assertValid(); 72 73 void copyHeaders(WebSocketFrame frame) { 74 finRsvOp = 0x00; 75 finRsvOp |= frame.isFin() ? 0x80 : 0x00; 76 finRsvOp |= frame.isRsv1() ? 0x40 : 0x00; 77 finRsvOp |= frame.isRsv2() ? 0x20 : 0x00; 78 finRsvOp |= frame.isRsv3() ? 0x10 : 0x00; 79 finRsvOp |= frame.getOpCode() & 0x0F; 80 81 masked = frame.isMasked(); 82 if (masked) { 83 mask = frame.getMask(); 84 } else { 85 mask = null; 86 } 87 } 88 89 protected void copyHeaders(AbstractWebSocketFrame copy) { 90 finRsvOp = copy.finRsvOp; 91 masked = copy.masked; 92 mask = null; 93 if (copy.mask !is null) 94 mask = copy.mask.dup; 95 } 96 97 bool equals(Object obj) { return opEquals(obj); } 98 99 override bool opEquals(Object obj) { 100 if (this is obj) { 101 return true; 102 } 103 if (obj is null) { 104 return false; 105 } 106 107 AbstractWebSocketFrame other = cast(AbstractWebSocketFrame) obj; 108 if(other is null) return false; 109 110 if (data is null) { 111 if (other.data !is null) { 112 return false; 113 } 114 } else if (!data.opEquals(other.data)) { 115 return false; 116 } 117 if (finRsvOp != other.finRsvOp) { 118 return false; 119 } 120 if (mask != other.mask) { 121 return false; 122 } 123 if (masked != other.masked) { 124 return false; 125 } 126 return true; 127 } 128 129 override 130 byte[] getMask() { 131 return mask; 132 } 133 134 override 135 final byte getOpCode() { 136 return cast(byte) (finRsvOp & 0x0F); 137 } 138 139 /** 140 * Get the payload ByteBuffer. possible null. 141 */ 142 override 143 ByteBuffer getPayload() { 144 return data; 145 } 146 147 string getPayloadAsUTF8() { 148 return BufferUtils.toString(getPayload()); 149 } 150 151 override 152 int getPayloadLength() { 153 if (data is null) { 154 return 0; 155 } 156 return data.remaining(); 157 } 158 159 override 160 WebSocketFrameType getType() { 161 return FrameTypeHelper.from(getOpCode()); 162 } 163 164 size_t hashCode() { return toHash(); } 165 166 override size_t toHash() @trusted nothrow { 167 int prime = 31; 168 size_t result = 1; 169 result = (prime * result) + ((data is null) ? 0 : data.toHash()); 170 result = (prime * result) + finRsvOp; 171 result = (prime * result) + hashOf(mask); 172 return result; 173 } 174 175 override 176 bool hasPayload() { 177 return ((data !is null) && data.hasRemaining()); 178 } 179 180 abstract bool isControlFrame(); 181 182 abstract bool isDataFrame(); 183 184 override 185 bool isFin() { 186 return cast(byte) (finRsvOp & 0x80) != 0; 187 } 188 189 override 190 bool isMasked() { 191 return masked; 192 } 193 194 override 195 bool isRsv1() { 196 return cast(byte) (finRsvOp & 0x40) != 0; 197 } 198 199 override 200 bool isRsv2() { 201 return cast(byte) (finRsvOp & 0x20) != 0; 202 } 203 204 override 205 bool isRsv3() { 206 return cast(byte) (finRsvOp & 0x10) != 0; 207 } 208 209 void reset() { 210 finRsvOp = cast(byte) 0x80; // FIN (!RSV, opcode 0) 211 masked = false; 212 data = null; 213 mask = null; 214 } 215 216 AbstractWebSocketFrame setFin(bool fin) { 217 // set bit 1 218 this.finRsvOp = cast(byte) ((finRsvOp & 0x7F) | (fin ? 0x80 : 0x00)); 219 return this; 220 } 221 222 WebSocketFrame setMask(byte[] maskingKey) { 223 this.mask = maskingKey; 224 this.masked = (mask !is null); 225 return this; 226 } 227 228 WebSocketFrame setMasked(bool mask) { 229 this.masked = mask; 230 return this; 231 } 232 233 protected AbstractWebSocketFrame setOpCode(byte op) { 234 this.finRsvOp = cast(byte) ((finRsvOp & 0xF0) | (op & 0x0F)); 235 return this; 236 } 237 238 /** 239 * Set the data payload. 240 * <p> 241 * The provided buffer will be used as is, no copying of bytes performed. 242 * <p> 243 * The provided buffer should be flipped and ready to READ from. 244 * 245 * @param buf the bytebuffer to set 246 * @return the frame itself 247 */ 248 AbstractWebSocketFrame setPayload(ByteBuffer buf) { 249 data = buf; 250 return this; 251 } 252 253 AbstractWebSocketFrame setRsv1(bool rsv1) { 254 // set bit 2 255 this.finRsvOp = cast(byte) ((finRsvOp & 0xBF) | (rsv1 ? 0x40 : 0x00)); 256 return this; 257 } 258 259 AbstractWebSocketFrame setRsv2(bool rsv2) { 260 // set bit 3 261 this.finRsvOp = cast(byte) ((finRsvOp & 0xDF) | (rsv2 ? 0x20 : 0x00)); 262 return this; 263 } 264 265 AbstractWebSocketFrame setRsv3(bool rsv3) { 266 // set bit 4 267 this.finRsvOp = cast(byte) ((finRsvOp & 0xEF) | (rsv3 ? 0x10 : 0x00)); 268 return this; 269 } 270 271 override 272 string toString() { 273 StringBuilder b = new StringBuilder(); 274 b.append(OpCode.name(cast(byte) (finRsvOp & 0x0F))); 275 b.append('['); 276 b.append("len=").append(getPayloadLength()); 277 b.append(",fin=").append((finRsvOp & 0x80) != 0); 278 b.append(",rsv="); 279 b.append(((finRsvOp & 0x40) != 0) ? '1' : '.'); 280 b.append(((finRsvOp & 0x20) != 0) ? '1' : '.'); 281 b.append(((finRsvOp & 0x10) != 0) ? '1' : '.'); 282 b.append(",masked=").append(masked); 283 b.append(']'); 284 return b.toString(); 285 } 286 } 287 288 /** 289 * 290 */ 291 class FrameTypeHelper { 292 293 static WebSocketFrameType from(byte op) { 294 foreach (WebSocketFrameType type ; EnumMembers!(WebSocketFrameType)) { 295 if (cast(byte)type == op) 296 return type; 297 } 298 throw new IllegalArgumentException("OpCode " ~ to!string(op) ~ 299 " is not a valid WebSocketFrameType"); 300 } 301 302 static bool isControl(WebSocketFrameType type) { 303 return type >= WebSocketFrameType.CLOSE; 304 } 305 306 static bool isData(WebSocketFrameType type) { 307 return (type == WebSocketFrameType.TEXT) || (type == WebSocketFrameType.BINARY); 308 } 309 310 static bool isContinuation(WebSocketFrameType type) { 311 return type == WebSocketFrameType.CONTINUATION; 312 } 313 }