View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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 copy)
60      {
61          WebSocketFrame frame = null;
62          switch (copy.getOpCode())
63          {
64              case OpCode.BINARY:
65                  frame = new BinaryFrame();
66                  break;
67              case OpCode.TEXT:
68                  frame = new TextFrame();
69                  break;
70              case OpCode.CLOSE:
71                  frame = new CloseFrame();
72                  break;
73              case OpCode.CONTINUATION:
74                  frame = new ContinuationFrame();
75                  break;
76              case OpCode.PING:
77                  frame = new PingFrame();
78                  break;
79              case OpCode.PONG:
80                  frame = new PongFrame();
81                  break;
82              default:
83                  throw new IllegalArgumentException("Cannot copy frame with opcode " + copy.getOpCode() + " - " + copy);
84          }
85  
86          frame.copyHeaders(copy);
87          frame.setPayload(copy.getPayload());
88  
89          return frame;
90      }
91  
92      /**
93       * Combined FIN + RSV1 + RSV2 + RSV3 + OpCode byte.
94       * <p>
95       * 
96       * <pre>
97       *   1000_0000 (0x80) = fin
98       *   0100_0000 (0x40) = rsv1
99       *   0010_0000 (0x20) = rsv2
100      *   0001_0000 (0x10) = rsv3
101      *   0000_1111 (0x0F) = opcode
102      * </pre>
103      */
104     protected byte finRsvOp;
105     protected boolean masked = false;
106 
107     protected byte mask[];
108     /**
109      * The payload data.
110      * <p>
111      * It is assumed to always be in FLUSH mode (ready to read) in this object.
112      */
113     protected ByteBuffer data;
114 
115     protected int payloadLength = 0;
116 
117     /**
118      * Construct form opcode
119      */
120     protected WebSocketFrame(byte opcode)
121     {
122         reset();
123         setOpCode(opcode);
124     }
125 
126     public abstract void assertValid();
127 
128     protected void copy(WebSocketFrame copy, ByteBuffer payload)
129     {
130         copyHeaders(copy);
131         setPayload(payload);
132     }
133 
134     protected void copyHeaders(Frame frame)
135     {
136         finRsvOp = 0x00;
137         finRsvOp |= frame.isFin()?0x80:0x00;
138         finRsvOp |= frame.isRsv1()?0x40:0x00;
139         finRsvOp |= frame.isRsv2()?0x20:0x00;
140         finRsvOp |= frame.isRsv3()?0x10:0x00;
141         finRsvOp |= frame.getOpCode() & 0x0F;
142 
143         masked = frame.isMasked();
144         if (masked)
145         {
146             mask = frame.getMask();
147         }
148         else
149         {
150             mask = null;
151         }
152     }
153 
154     protected void copyHeaders(WebSocketFrame copy)
155     {
156         finRsvOp = copy.finRsvOp;
157         masked = copy.masked;
158         mask = null;
159         if (copy.mask != null)
160         {
161             mask = new byte[copy.mask.length];
162             System.arraycopy(copy.mask,0,mask,0,mask.length);
163         }
164     }
165 
166     @Override
167     public boolean equals(Object obj)
168     {
169         if (this == obj)
170         {
171             return true;
172         }
173         if (obj == null)
174         {
175             return false;
176         }
177         if (getClass() != obj.getClass())
178         {
179             return false;
180         }
181         WebSocketFrame other = (WebSocketFrame)obj;
182         if (data == null)
183         {
184             if (other.data != null)
185             {
186                 return false;
187             }
188         }
189         else if (!data.equals(other.data))
190         {
191             return false;
192         }
193         if (finRsvOp != other.finRsvOp)
194         {
195             return false;
196         }
197         if (!Arrays.equals(mask,other.mask))
198         {
199             return false;
200         }
201         if (masked != other.masked)
202         {
203             return false;
204         }
205         return true;
206     }
207 
208     @Override
209     public byte[] getMask()
210     {
211         return mask;
212     }
213 
214     @Override
215     public final byte getOpCode()
216     {
217         return (byte)(finRsvOp & 0x0F);
218     }
219 
220     /**
221      * Get the payload ByteBuffer. possible null.
222      * <p>
223      * 
224      * @return A {@link ByteBuffer#slice()} of the payload buffer (to prevent modification of the buffer state). Possibly null if no payload present.
225      *         <p>
226      *         Note: this method is exposed via the immutable {@link Frame#getPayload()} method.
227      */
228     @Override
229     public ByteBuffer getPayload()
230     {
231         return data;
232     }
233 
234     public String getPayloadAsUTF8()
235     {
236         return BufferUtil.toUTF8String(getPayload());
237     }
238 
239     @Override
240     public int getPayloadLength()
241     {
242         if (data == null)
243         {
244             return 0;
245         }
246         return payloadLength;
247     }
248 
249     @Override
250     public Type getType()
251     {
252         return Type.from(getOpCode());
253     }
254 
255     @Override
256     public int hashCode()
257     {
258         final int prime = 31;
259         int result = 1;
260         result = (prime * result) + ((data == null)?0:data.hashCode());
261         result = (prime * result) + finRsvOp;
262         result = (prime * result) + Arrays.hashCode(mask);
263         return result;
264     }
265 
266     @Override
267     public boolean hasPayload()
268     {
269         return ((data != null) && (payloadLength > 0));
270     }
271 
272     public abstract boolean isControlFrame();
273 
274     public abstract boolean isDataFrame();
275 
276     @Override
277     public boolean isFin()
278     {
279         return (byte)(finRsvOp & 0x80) != 0;
280     }
281 
282     @Override
283     public boolean isLast()
284     {
285         return isFin();
286     }
287 
288     @Override
289     public boolean isMasked()
290     {
291         return masked;
292     }
293 
294     @Override
295     public boolean isRsv1()
296     {
297         return (byte)(finRsvOp & 0x40) != 0;
298     }
299 
300     @Override
301     public boolean isRsv2()
302     {
303         return (byte)(finRsvOp & 0x20) != 0;
304     }
305 
306     @Override
307     public boolean isRsv3()
308     {
309         return (byte)(finRsvOp & 0x10) != 0;
310     }
311 
312     /**
313      * Get the position currently within the payload data.
314      * <p>
315      * Used by flow control, generator and window sizing.
316      * 
317      * @return the number of bytes remaining in the payload data that has not yet been written out to Network ByteBuffers.
318      */
319     public int position()
320     {
321         if (data == null)
322         {
323             return -1;
324         }
325         return data.position();
326     }
327 
328     /**
329      * Get the number of bytes remaining to write out to the Network ByteBuffer.
330      * <p>
331      * Used by flow control, generator and window sizing.
332      * 
333      * @return the number of bytes remaining in the payload data that has not yet been written out to Network ByteBuffers.
334      */
335     @Override
336     public int remaining()
337     {
338         if (data == null)
339         {
340             return 0;
341         }
342         return data.remaining();
343     }
344 
345     public void reset()
346     {
347         finRsvOp = (byte)0x80; // FIN (!RSV, opcode 0)
348         masked = false;
349         data = null;
350         payloadLength = 0;
351         mask = null;
352     }
353 
354     public WebSocketFrame setFin(boolean fin)
355     {
356         // set bit 1
357         this.finRsvOp = (byte)((finRsvOp & 0x7F) | (fin?0x80:0x00));
358         return this;
359     }
360 
361     public Frame setMask(byte[] maskingKey)
362     {
363         this.mask = maskingKey;
364         this.masked = (mask != null);
365         return this;
366     }
367 
368     public Frame setMasked(boolean mask)
369     {
370         this.masked = mask;
371         return this;
372     }
373 
374     protected WebSocketFrame setOpCode(byte op)
375     {
376         this.finRsvOp = (byte)((finRsvOp & 0xF0) | (op & 0x0F));
377         return this;
378     }
379 
380     /**
381      * Set the data payload.
382      * <p>
383      * The provided buffer will be used as is, no copying of bytes performed.
384      * <p>
385      * The provided buffer should be flipped and ready to READ from.
386      * 
387      * @param buf
388      *            the bytebuffer to set
389      */
390     public WebSocketFrame setPayload(ByteBuffer buf)
391     {
392         if (buf == null)
393         {
394             data = null;
395             return this;
396         }
397 
398         data = buf.slice();
399         payloadLength = data.limit();
400         return this;
401     }
402 
403     public WebSocketFrame setRsv1(boolean rsv1)
404     {
405         // set bit 2
406         this.finRsvOp = (byte)((finRsvOp & 0xBF) | (rsv1?0x40:0x00));
407         return this;
408     }
409 
410     public WebSocketFrame setRsv2(boolean rsv2)
411     {
412         // set bit 3
413         this.finRsvOp = (byte)((finRsvOp & 0xDF) | (rsv2?0x20:0x00));
414         return this;
415     }
416 
417     public WebSocketFrame setRsv3(boolean rsv3)
418     {
419         // set bit 4
420         this.finRsvOp = (byte)((finRsvOp & 0xEF) | (rsv3?0x10:0x00));
421         return this;
422     }
423 
424     @Override
425     public String toString()
426     {
427         StringBuilder b = new StringBuilder();
428         b.append(OpCode.name((byte)(finRsvOp & 0x0F)));
429         b.append('[');
430         b.append("len=").append(payloadLength);
431         b.append(",fin=").append((finRsvOp & 0x80) != 0);
432         b.append(",rsv=");
433         b.append(((finRsvOp & 0x40) != 0)?'1':'.');
434         b.append(((finRsvOp & 0x20) != 0)?'1':'.');
435         b.append(((finRsvOp & 0x10) != 0)?'1':'.');
436         b.append(",masked=").append(masked);
437         b.append(",remaining=").append(remaining());
438         b.append(",position=").append(position());
439         b.append(']');
440         return b.toString();
441     }
442 }