View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.websocket.common;
20  
21  import java.nio.ByteBuffer;
22  import java.util.Arrays;
23  
24  import org.eclipse.jetty.util.BufferUtil;
25  import org.eclipse.jetty.websocket.api.extensions.Frame;
26  import org.eclipse.jetty.websocket.common.frames.BinaryFrame;
27  import org.eclipse.jetty.websocket.common.frames.CloseFrame;
28  import org.eclipse.jetty.websocket.common.frames.ContinuationFrame;
29  import org.eclipse.jetty.websocket.common.frames.PingFrame;
30  import org.eclipse.jetty.websocket.common.frames.PongFrame;
31  import org.eclipse.jetty.websocket.common.frames.TextFrame;
32  
33  /**
34   * A Base Frame as seen in <a href="https://tools.ietf.org/html/rfc6455#section-5.2">RFC 6455. Sec 5.2</a>
35   * 
36   * <pre>
37   *    0                   1                   2                   3
38   *    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
39   *   +-+-+-+-+-------+-+-------------+-------------------------------+
40   *   |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
41   *   |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
42   *   |N|V|V|V|       |S|             |   (if payload len==126/127)   |
43   *   | |1|2|3|       |K|             |                               |
44   *   +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
45   *   |     Extended payload length continued, if payload len == 127  |
46   *   + - - - - - - - - - - - - - - - +-------------------------------+
47   *   |                               |Masking-key, if MASK set to 1  |
48   *   +-------------------------------+-------------------------------+
49   *   | Masking-key (continued)       |          Payload Data         |
50   *   +-------------------------------- - - - - - - - - - - - - - - - +
51   *   :                     Payload Data continued ...                :
52   *   + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
53   *   |                     Payload Data continued ...                |
54   *   +---------------------------------------------------------------+
55   * </pre>
56   */
57  public abstract class WebSocketFrame implements Frame
58  {
59      public static WebSocketFrame copy(Frame original)
60      {
61          WebSocketFrame copy;
62          switch (original.getOpCode())
63          {
64              case OpCode.BINARY:
65                  copy = new BinaryFrame();
66                  break;
67              case OpCode.TEXT:
68                  copy = new TextFrame();
69                  break;
70              case OpCode.CLOSE:
71                  copy = new CloseFrame();
72                  break;
73              case OpCode.CONTINUATION:
74                  copy = new ContinuationFrame();
75                  break;
76              case OpCode.PING:
77                  copy = new PingFrame();
78                  break;
79              case OpCode.PONG:
80                  copy = new PongFrame();
81                  break;
82              default:
83                  throw new IllegalArgumentException("Cannot copy frame with opcode " + original.getOpCode() + " - " + original);
84          }
85  
86          copy.copyHeaders(original);
87          ByteBuffer payload = original.getPayload();
88          if (payload != null)
89          {
90              ByteBuffer payloadCopy = ByteBuffer.allocate(payload.remaining());
91              payloadCopy.put(payload.slice()).flip();
92              copy.setPayload(payloadCopy);
93          }
94          return copy;
95      }
96  
97      /**
98       * Combined FIN + RSV1 + RSV2 + RSV3 + OpCode byte.
99       * 
100      * <pre>
101      *   1000_0000 (0x80) = fin
102      *   0100_0000 (0x40) = rsv1
103      *   0010_0000 (0x20) = rsv2
104      *   0001_0000 (0x10) = rsv3
105      *   0000_1111 (0x0F) = opcode
106      * </pre>
107      */
108     protected byte finRsvOp;
109     protected boolean masked = false;
110 
111     protected byte mask[];
112     /**
113      * The payload data.
114      * <p>
115      * It is assumed to always be in FLUSH mode (ready to read) in this object.
116      */
117     protected ByteBuffer data;
118 
119     /**
120      * Construct form opcode
121      * @param opcode the opcode the frame is based on
122      */
123     protected WebSocketFrame(byte opcode)
124     {
125         reset();
126         setOpCode(opcode);
127     }
128 
129     public abstract void assertValid();
130 
131     protected void copyHeaders(Frame frame)
132     {
133         finRsvOp = 0x00;
134         finRsvOp |= frame.isFin()?0x80:0x00;
135         finRsvOp |= frame.isRsv1()?0x40:0x00;
136         finRsvOp |= frame.isRsv2()?0x20:0x00;
137         finRsvOp |= frame.isRsv3()?0x10:0x00;
138         finRsvOp |= frame.getOpCode() & 0x0F;
139 
140         masked = frame.isMasked();
141         if (masked)
142         {
143             mask = frame.getMask();
144         }
145         else
146         {
147             mask = null;
148         }
149     }
150 
151     protected void copyHeaders(WebSocketFrame copy)
152     {
153         finRsvOp = copy.finRsvOp;
154         masked = copy.masked;
155         mask = null;
156         if (copy.mask != null)
157             mask = Arrays.copyOf(copy.mask, copy.mask.length);
158     }
159 
160     @Override
161     public boolean equals(Object obj)
162     {
163         if (this == obj)
164         {
165             return true;
166         }
167         if (obj == null)
168         {
169             return false;
170         }
171         if (getClass() != obj.getClass())
172         {
173             return false;
174         }
175         WebSocketFrame other = (WebSocketFrame)obj;
176         if (data == null)
177         {
178             if (other.data != null)
179             {
180                 return false;
181             }
182         }
183         else if (!data.equals(other.data))
184         {
185             return false;
186         }
187         if (finRsvOp != other.finRsvOp)
188         {
189             return false;
190         }
191         if (!Arrays.equals(mask,other.mask))
192         {
193             return false;
194         }
195         if (masked != other.masked)
196         {
197             return false;
198         }
199         return true;
200     }
201 
202     @Override
203     public byte[] getMask()
204     {
205         return mask;
206     }
207 
208     @Override
209     public final byte getOpCode()
210     {
211         return (byte)(finRsvOp & 0x0F);
212     }
213 
214     /**
215      * Get the payload ByteBuffer. possible null.
216      */
217     @Override
218     public ByteBuffer getPayload()
219     {
220         return data;
221     }
222 
223     public String getPayloadAsUTF8()
224     {
225         return BufferUtil.toUTF8String(getPayload());
226     }
227 
228     @Override
229     public int getPayloadLength()
230     {
231         if (data == null)
232         {
233             return 0;
234         }
235         return data.remaining();
236     }
237 
238     @Override
239     public Type getType()
240     {
241         return Type.from(getOpCode());
242     }
243 
244     @Override
245     public int hashCode()
246     {
247         final int prime = 31;
248         int result = 1;
249         result = (prime * result) + ((data == null)?0:data.hashCode());
250         result = (prime * result) + finRsvOp;
251         result = (prime * result) + Arrays.hashCode(mask);
252         return result;
253     }
254 
255     @Override
256     public boolean hasPayload()
257     {
258         return ((data != null) && data.hasRemaining());
259     }
260 
261     public abstract boolean isControlFrame();
262 
263     public abstract boolean isDataFrame();
264 
265     @Override
266     public boolean isFin()
267     {
268         return (byte)(finRsvOp & 0x80) != 0;
269     }
270 
271     @Override
272     public boolean isLast()
273     {
274         return isFin();
275     }
276 
277     @Override
278     public boolean isMasked()
279     {
280         return masked;
281     }
282 
283     @Override
284     public boolean isRsv1()
285     {
286         return (byte)(finRsvOp & 0x40) != 0;
287     }
288 
289     @Override
290     public boolean isRsv2()
291     {
292         return (byte)(finRsvOp & 0x20) != 0;
293     }
294 
295     @Override
296     public boolean isRsv3()
297     {
298         return (byte)(finRsvOp & 0x10) != 0;
299     }
300 
301     public void reset()
302     {
303         finRsvOp = (byte)0x80; // FIN (!RSV, opcode 0)
304         masked = false;
305         data = null;
306         mask = null;
307     }
308 
309     public WebSocketFrame setFin(boolean fin)
310     {
311         // set bit 1
312         this.finRsvOp = (byte)((finRsvOp & 0x7F) | (fin?0x80:0x00));
313         return this;
314     }
315 
316     public Frame setMask(byte[] maskingKey)
317     {
318         this.mask = maskingKey;
319         this.masked = (mask != null);
320         return this;
321     }
322 
323     public Frame setMasked(boolean mask)
324     {
325         this.masked = mask;
326         return this;
327     }
328 
329     protected WebSocketFrame setOpCode(byte op)
330     {
331         this.finRsvOp = (byte)((finRsvOp & 0xF0) | (op & 0x0F));
332         return this;
333     }
334 
335     /**
336      * Set the data payload.
337      * <p>
338      * The provided buffer will be used as is, no copying of bytes performed.
339      * <p>
340      * The provided buffer should be flipped and ready to READ from.
341      * 
342      * @param buf
343      *            the bytebuffer to set
344      * @return the frame itself
345      */
346     public WebSocketFrame setPayload(ByteBuffer buf)
347     {
348         data = buf;
349         return this;
350     }
351 
352     public WebSocketFrame setRsv1(boolean rsv1)
353     {
354         // set bit 2
355         this.finRsvOp = (byte)((finRsvOp & 0xBF) | (rsv1?0x40:0x00));
356         return this;
357     }
358 
359     public WebSocketFrame setRsv2(boolean rsv2)
360     {
361         // set bit 3
362         this.finRsvOp = (byte)((finRsvOp & 0xDF) | (rsv2?0x20:0x00));
363         return this;
364     }
365 
366     public WebSocketFrame setRsv3(boolean rsv3)
367     {
368         // set bit 4
369         this.finRsvOp = (byte)((finRsvOp & 0xEF) | (rsv3?0x10:0x00));
370         return this;
371     }
372 
373     @Override
374     public String toString()
375     {
376         StringBuilder b = new StringBuilder();
377         b.append(OpCode.name((byte)(finRsvOp & 0x0F)));
378         b.append('[');
379         b.append("len=").append(getPayloadLength());
380         b.append(",fin=").append((finRsvOp & 0x80) != 0);
381         b.append(",rsv=");
382         b.append(((finRsvOp & 0x40) != 0)?'1':'.');
383         b.append(((finRsvOp & 0x20) != 0)?'1':'.');
384         b.append(((finRsvOp & 0x10) != 0)?'1':'.');
385         b.append(",masked=").append(masked);
386         b.append(']');
387         return b.toString();
388     }
389 }