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 }