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.util.StringUtil;
26  import org.eclipse.jetty.websocket.api.ProtocolException;
27  import org.eclipse.jetty.websocket.api.extensions.Frame;
28  
29  /**
30   * A Base Frame as seen in <a href="https://tools.ietf.org/html/rfc6455#section-5.2">RFC 6455. Sec 5.2</a>
31   * 
32   * <pre>
33   *    0                   1                   2                   3
34   *    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
35   *   +-+-+-+-+-------+-+-------------+-------------------------------+
36   *   |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
37   *   |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
38   *   |N|V|V|V|       |S|             |   (if payload len==126/127)   |
39   *   | |1|2|3|       |K|             |                               |
40   *   +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
41   *   |     Extended payload length continued, if payload len == 127  |
42   *   + - - - - - - - - - - - - - - - +-------------------------------+
43   *   |                               |Masking-key, if MASK set to 1  |
44   *   +-------------------------------+-------------------------------+
45   *   | Masking-key (continued)       |          Payload Data         |
46   *   +-------------------------------- - - - - - - - - - - - - - - - +
47   *   :                     Payload Data continued ...                :
48   *   + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
49   *   |                     Payload Data continued ...                |
50   *   +---------------------------------------------------------------+
51   * </pre>
52   */
53  public class WebSocketFrame implements Frame
54  {
55      /** Maximum size of Control frame, per RFC 6455 */
56      public static final int MAX_CONTROL_PAYLOAD = 125;
57  
58      public static WebSocketFrame binary()
59      {
60          return new WebSocketFrame(OpCode.BINARY);
61      }
62  
63      public static WebSocketFrame binary(byte buf[])
64      {
65          return new WebSocketFrame(OpCode.BINARY).setPayload(buf);
66      }
67  
68      public static WebSocketFrame ping()
69      {
70          return new WebSocketFrame(OpCode.PING);
71      }
72  
73      public static WebSocketFrame pong()
74      {
75          return new WebSocketFrame(OpCode.PONG);
76      }
77  
78      public static WebSocketFrame text()
79      {
80          return new WebSocketFrame(OpCode.TEXT);
81      }
82  
83      public static WebSocketFrame text(String msg)
84      {
85          return new WebSocketFrame(OpCode.TEXT).setPayload(msg);
86      }
87  
88      private boolean fin = true;
89      private boolean rsv1 = false;
90      private boolean rsv2 = false;
91      private boolean rsv3 = false;
92      protected byte opcode = OpCode.UNDEFINED;
93      private boolean masked = false;
94      private byte mask[];
95      /**
96       * The payload data.
97       * <p>
98       * It is assumed to always be in FLUSH mode (ready to read) in this object.
99       */
100     private ByteBuffer data;
101     private int payloadLength = 0;
102     /** position of start of data within a fresh payload */
103     private int payloadStart = -1;
104 
105     private Type type;
106     private boolean continuation = false;
107     private int continuationIndex = 0;
108 
109     /**
110      * Default constructor
111      */
112     public WebSocketFrame()
113     {
114         this(OpCode.UNDEFINED);
115     }
116 
117     /**
118      * Construct form opcode
119      */
120     public WebSocketFrame(byte opcode)
121     {
122         reset();
123         setOpCode(opcode);
124     }
125 
126     /**
127      * Copy constructor for the websocket frame.
128      * 
129      * @param copy
130      *            the websocket to copy.
131      */
132     public WebSocketFrame(Frame frame)
133     {
134         if (frame instanceof WebSocketFrame)
135         {
136             WebSocketFrame wsf = (WebSocketFrame)frame;
137             copy(wsf,wsf.data);
138         }
139         else
140         {
141             // Copy manually
142             fin = frame.isFin();
143             rsv1 = frame.isRsv1();
144             rsv2 = frame.isRsv2();
145             rsv3 = frame.isRsv3();
146             opcode = frame.getType().getOpCode();
147             type = frame.getType();
148             masked = frame.isMasked();
149             mask = null;
150             byte maskCopy[] = frame.getMask();
151             if (maskCopy != null)
152             {
153                 mask = new byte[maskCopy.length];
154                 System.arraycopy(maskCopy,0,mask,0,mask.length);
155             }
156 
157             setPayload(frame.getPayload());
158         }
159     }
160 
161     /**
162      * Copy constructor for the websocket frame.
163      * <p>
164      * Note: the underlying payload is merely a {@link ByteBuffer#slice()} of the input frame.
165      * 
166      * @param copy
167      *            the websocket to copy.
168      */
169     public WebSocketFrame(WebSocketFrame copy)
170     {
171         copy(copy,copy.data);
172     }
173 
174     /**
175      * Copy constructor for the websocket frame, with an alternate payload.
176      * <p>
177      * This is especially useful for Extensions to utilize when mutating the payload.
178      * 
179      * @param copy
180      *            the websocket to copy.
181      * @param altPayload
182      *            the alternate payload to use for this frame.
183      */
184     public WebSocketFrame(WebSocketFrame copy, ByteBuffer altPayload)
185     {
186         copy(copy,altPayload);
187     }
188 
189     public void assertValid()
190     {
191         if (OpCode.isControlFrame(opcode))
192         {
193             if (getPayloadLength() > WebSocketFrame.MAX_CONTROL_PAYLOAD)
194             {
195                 throw new ProtocolException("Desired payload length [" + getPayloadLength() + "] exceeds maximum control payload length ["
196                         + MAX_CONTROL_PAYLOAD + "]");
197             }
198 
199             if (fin == false)
200             {
201                 throw new ProtocolException("Cannot have FIN==false on Control frames");
202             }
203 
204             if (rsv1 == true)
205             {
206                 throw new ProtocolException("Cannot have RSV1==true on Control frames");
207             }
208 
209             if (rsv2 == true)
210             {
211                 throw new ProtocolException("Cannot have RSV2==true on Control frames");
212             }
213 
214             if (rsv3 == true)
215             {
216                 throw new ProtocolException("Cannot have RSV3==true on Control frames");
217             }
218 
219             if (isContinuation())
220             {
221                 throw new ProtocolException("Control frames cannot be Continuations");
222             }
223         }
224     }
225 
226     private final void copy(WebSocketFrame copy, ByteBuffer payload)
227     {
228         fin = copy.fin;
229         rsv1 = copy.rsv1;
230         rsv2 = copy.rsv2;
231         rsv3 = copy.rsv3;
232         opcode = copy.opcode;
233         type = copy.type;
234         masked = copy.masked;
235         mask = null;
236         if (copy.mask != null)
237         {
238             mask = new byte[copy.mask.length];
239             System.arraycopy(copy.mask,0,mask,0,mask.length);
240         }
241         continuationIndex = copy.continuationIndex;
242         continuation = copy.continuation;
243 
244         setPayload(payload);
245     }
246 
247     @Override
248     public boolean equals(Object obj)
249     {
250         if (this == obj)
251         {
252             return true;
253         }
254         if (obj == null)
255         {
256             return false;
257         }
258         if (getClass() != obj.getClass())
259         {
260             return false;
261         }
262         WebSocketFrame other = (WebSocketFrame)obj;
263         if (continuation != other.continuation)
264         {
265             return false;
266         }
267         if (continuationIndex != other.continuationIndex)
268         {
269             return false;
270         }
271         if (data == null)
272         {
273             if (other.data != null)
274             {
275                 return false;
276             }
277         }
278         else if (!data.equals(other.data))
279         {
280             return false;
281         }
282         if (fin != other.fin)
283         {
284             return false;
285         }
286         if (!Arrays.equals(mask,other.mask))
287         {
288             return false;
289         }
290         if (masked != other.masked)
291         {
292             return false;
293         }
294         if (opcode != other.opcode)
295         {
296             return false;
297         }
298         if (rsv1 != other.rsv1)
299         {
300             return false;
301         }
302         if (rsv2 != other.rsv2)
303         {
304             return false;
305         }
306         if (rsv3 != other.rsv3)
307         {
308             return false;
309         }
310         return true;
311     }
312 
313     /**
314      * The number of fragments this frame consists of.
315      * <p>
316      * For every {@link OpCode#CONTINUATION} opcode encountered, this increments by one.
317      * <p>
318      * Note: Not part of the Base Framing Protocol / header information.
319      * 
320      * @return the number of continuation fragments encountered.
321      */
322     public int getContinuationIndex()
323     {
324         return continuationIndex;
325     }
326 
327     @Override
328     public byte[] getMask()
329     {
330         if (!masked)
331         {
332             throw new IllegalStateException("Frame is not masked");
333         }
334         return mask;
335     }
336 
337     @Override
338     public final byte getOpCode()
339     {
340         return opcode;
341     }
342 
343     /**
344      * Get the payload ByteBuffer. possible null.
345      * <p>
346      * 
347      * @return A {@link ByteBuffer#slice()} of the payload buffer (to prevent modification of the buffer state). Possibly null if no payload present.
348      *         <p>
349      *         Note: this method is exposed via the immutable {@link Frame#getPayload()} method.
350      */
351     @Override
352     public ByteBuffer getPayload()
353     {
354         if (data != null)
355         {
356             return data;
357         }
358         else
359         {
360             return null;
361         }
362     }
363 
364     public String getPayloadAsUTF8()
365     {
366         if (data == null)
367         {
368             return null;
369         }
370         return BufferUtil.toUTF8String(data);
371     }
372 
373     @Override
374     public int getPayloadLength()
375     {
376         if (data == null)
377         {
378             return 0;
379         }
380         return payloadLength;
381     }
382 
383     @Override
384     public int getPayloadStart()
385     {
386         if (data == null)
387         {
388             return -1;
389         }
390         return payloadStart;
391     }
392 
393     @Override
394     public Type getType()
395     {
396         return type;
397     }
398 
399     @Override
400     public int hashCode()
401     {
402         final int prime = 31;
403         int result = 1;
404         result = (prime * result) + (continuation?1231:1237);
405         result = (prime * result) + continuationIndex;
406         result = (prime * result) + ((data == null)?0:data.hashCode());
407         result = (prime * result) + (fin?1231:1237);
408         result = (prime * result) + Arrays.hashCode(mask);
409         result = (prime * result) + (masked?1231:1237);
410         result = (prime * result) + opcode;
411         result = (prime * result) + (rsv1?1231:1237);
412         result = (prime * result) + (rsv2?1231:1237);
413         result = (prime * result) + (rsv3?1231:1237);
414         return result;
415     }
416 
417     @Override
418     public boolean hasPayload()
419     {
420         return ((data != null) && (payloadLength > 0));
421     }
422 
423     @Override
424     public boolean isContinuation()
425     {
426         return continuation;
427     }
428 
429     public boolean isControlFrame()
430     {
431         return OpCode.isControlFrame(opcode);
432     }
433 
434     public boolean isDataFrame()
435     {
436         return OpCode.isDataFrame(opcode);
437     }
438 
439     @Override
440     public boolean isFin()
441     {
442         return fin;
443     }
444 
445     @Override
446     public boolean isLast()
447     {
448         return fin;
449     }
450 
451     public boolean isLastFrame()
452     {
453         return fin;
454     }
455 
456     @Override
457     public boolean isMasked()
458     {
459         return masked;
460     }
461 
462     @Override
463     public boolean isRsv1()
464     {
465         return rsv1;
466     }
467 
468     @Override
469     public boolean isRsv2()
470     {
471         return rsv2;
472     }
473 
474     @Override
475     public boolean isRsv3()
476     {
477         return rsv3;
478     }
479 
480     /**
481      * Get the position currently within the payload data.
482      * <p>
483      * Used by flow control, generator and window sizing.
484      * 
485      * @return the number of bytes remaining in the payload data that has not yet been written out to Network ByteBuffers.
486      */
487     public int position()
488     {
489         if (data == null)
490         {
491             return -1;
492         }
493         return data.position();
494     }
495 
496     /**
497      * Get the number of bytes remaining to write out to the Network ByteBuffer.
498      * <p>
499      * Used by flow control, generator and window sizing.
500      * 
501      * @return the number of bytes remaining in the payload data that has not yet been written out to Network ByteBuffers.
502      */
503     @Override
504     public int remaining()
505     {
506         if (data == null)
507         {
508             return 0;
509         }
510         return data.remaining();
511     }
512 
513     public void reset()
514     {
515         fin = true;
516         rsv1 = false;
517         rsv2 = false;
518         rsv3 = false;
519         opcode = -1;
520         masked = false;
521         data = null;
522         payloadLength = 0;
523         mask = null;
524         continuationIndex = 0;
525         continuation = false;
526     }
527 
528     public Frame setContinuation(boolean continuation)
529     {
530         this.continuation = continuation;
531         return this;
532     }
533 
534     public Frame setContinuationIndex(int continuationIndex)
535     {
536         this.continuationIndex = continuationIndex;
537         return this;
538     }
539 
540     public WebSocketFrame setFin(boolean fin)
541     {
542         this.fin = fin;
543         return this;
544     }
545 
546     public Frame setMask(byte[] maskingKey)
547     {
548         this.mask = maskingKey;
549         this.masked = (mask != null);
550         return this;
551     }
552 
553     public Frame setMasked(boolean mask)
554     {
555         this.masked = mask;
556         return this;
557     }
558 
559     public WebSocketFrame setOpCode(byte op)
560     {
561         this.opcode = op;
562 
563         if (op == OpCode.UNDEFINED)
564         {
565             this.type = null;
566         }
567         else
568         {
569             this.type = Frame.Type.from(op);
570         }
571         return this;
572     }
573 
574     /**
575      * Set the data and payload length.
576      * 
577      * @param buf
578      *            the bytebuffer to set
579      */
580     public WebSocketFrame setPayload(byte buf[])
581     {
582         if (buf == null)
583         {
584             data = null;
585             return this;
586         }
587 
588         if (OpCode.isControlFrame(opcode))
589         {
590             if (buf.length > WebSocketFrame.MAX_CONTROL_PAYLOAD)
591             {
592                 throw new ProtocolException("Control Payloads can not exceed 125 bytes in length.");
593             }
594         }
595 
596         data = BufferUtil.toBuffer(buf);
597         payloadStart = data.position();
598         payloadLength = data.limit();
599         return this;
600     }
601 
602     /**
603      * Set the data and payload length.
604      * 
605      * @param buf
606      *            the bytebuffer to set
607      */
608     public WebSocketFrame setPayload(byte buf[], int offset, int len)
609     {
610         if (buf == null)
611         {
612             data = null;
613             return this;
614         }
615 
616         if (OpCode.isControlFrame(opcode))
617         {
618             if (len > WebSocketFrame.MAX_CONTROL_PAYLOAD)
619             {
620                 throw new ProtocolException("Control Payloads can not exceed 125 bytes in length.");
621             }
622         }
623 
624         data = BufferUtil.toBuffer(buf,offset,len);
625         payloadStart = data.position();
626         payloadLength = data.limit();
627         return this;
628     }
629 
630     /**
631      * Set the data payload.
632      * <p>
633      * The provided buffer will be used as is, no copying of bytes performed.
634      * <p>
635      * The provided buffer should be flipped and ready to READ from.
636      * 
637      * @param buf
638      *            the bytebuffer to set
639      */
640     public WebSocketFrame setPayload(ByteBuffer buf)
641     {
642         if (buf == null)
643         {
644             data = null;
645             return this;
646         }
647 
648         if (OpCode.isControlFrame(opcode))
649         {
650             if (buf.remaining() > WebSocketFrame.MAX_CONTROL_PAYLOAD)
651             {
652                 throw new ProtocolException("Control Payloads can not exceed 125 bytes in length. (was " + buf.remaining() + " bytes)");
653             }
654         }
655 
656         data = buf.slice();
657         payloadStart = data.position();
658         payloadLength = data.limit();
659         return this;
660     }
661 
662     public WebSocketFrame setPayload(String str)
663     {
664         setPayload(BufferUtil.toBuffer(str,StringUtil.__UTF8_CHARSET));
665         return this;
666     }
667 
668     public WebSocketFrame setRsv1(boolean rsv1)
669     {
670         this.rsv1 = rsv1;
671         return this;
672     }
673 
674     public WebSocketFrame setRsv2(boolean rsv2)
675     {
676         this.rsv2 = rsv2;
677         return this;
678     }
679 
680     public WebSocketFrame setRsv3(boolean rsv3)
681     {
682         this.rsv3 = rsv3;
683         return this;
684     }
685 
686     @Override
687     public String toString()
688     {
689         StringBuilder b = new StringBuilder();
690         b.append(OpCode.name(opcode));
691         b.append('[');
692         b.append("len=").append(payloadLength);
693         b.append(",fin=").append(fin);
694         b.append(",rsv=");
695         b.append(rsv1?'1':'.');
696         b.append(rsv2?'1':'.');
697         b.append(rsv3?'1':'.');
698         b.append(",masked=").append(masked);
699         b.append(",continuation=").append(continuation);
700         b.append(",payloadStart=").append(getPayloadStart());
701         b.append(",remaining=").append(remaining());
702         b.append(",position=").append(position());
703         b.append(']');
704         return b.toString();
705     }
706 }