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