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