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.List;
23  
24  import org.eclipse.jetty.io.ByteBufferPool;
25  import org.eclipse.jetty.util.BufferUtil;
26  import org.eclipse.jetty.websocket.api.ProtocolException;
27  import org.eclipse.jetty.websocket.api.WebSocketBehavior;
28  import org.eclipse.jetty.websocket.api.WebSocketPolicy;
29  import org.eclipse.jetty.websocket.api.extensions.Extension;
30  import org.eclipse.jetty.websocket.api.extensions.Frame;
31  
32  /**
33   * Generating a frame in WebSocket land.
34   * 
35   * <pre>
36   *    0                   1                   2                   3
37   *    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
38   *   +-+-+-+-+-------+-+-------------+-------------------------------+
39   *   |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
40   *   |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
41   *   |N|V|V|V|       |S|             |   (if payload len==126/127)   |
42   *   | |1|2|3|       |K|             |                               |
43   *   +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
44   *   |     Extended payload length continued, if payload len == 127  |
45   *   + - - - - - - - - - - - - - - - +-------------------------------+
46   *   |                               |Masking-key, if MASK set to 1  |
47   *   +-------------------------------+-------------------------------+
48   *   | Masking-key (continued)       |          Payload Data         |
49   *   +-------------------------------- - - - - - - - - - - - - - - - +
50   *   :                     Payload Data continued ...                :
51   *   + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
52   *   |                     Payload Data continued ...                |
53   *   +---------------------------------------------------------------+
54   * </pre>
55   */
56  public class Generator
57  {
58      /**
59       * The overhead (maximum) for a framing header. Assuming a maximum sized payload with masking key.
60       */
61      public static final int MAX_HEADER_LENGTH = 28;
62  
63      private final WebSocketBehavior behavior;
64      private final ByteBufferPool bufferPool;
65      private final boolean validating;
66      private final boolean readOnly;
67  
68      /**
69       * Are any flags in use
70       * <p>
71       * 
72       * <pre>
73       *   0100_0000 (0x40) = rsv1
74       *   0010_0000 (0x20) = rsv2
75       *   0001_0000 (0x10) = rsv3
76       * </pre>
77       */
78      private byte flagsInUse = 0x00;
79  
80      /**
81       * Construct Generator with provided policy and bufferPool
82       * 
83       * @param policy
84       *            the policy to use
85       * @param bufferPool
86       *            the buffer pool to use
87       */
88      public Generator(WebSocketPolicy policy, ByteBufferPool bufferPool)
89      {
90          this(policy,bufferPool,true,false);
91      }
92  
93      /**
94       * Construct Generator with provided policy and bufferPool
95       * 
96       * @param policy
97       *            the policy to use
98       * @param bufferPool
99       *            the buffer pool to use
100      * @param validating
101      *            true to enable RFC frame validation
102      */
103     public Generator(WebSocketPolicy policy, ByteBufferPool bufferPool, boolean validating)
104     {
105         this(policy,bufferPool,validating,false);
106     }
107 
108     /**
109      * Construct Generator with provided policy and bufferPool
110      * 
111      * @param policy
112      *            the policy to use
113      * @param bufferPool
114      *            the buffer pool to use
115      * @param validating
116      *            true to enable RFC frame validation
117      * @param readOnly
118      *            true if generator is to treat frames as read-only and not modify them. Useful for debugging purposes, but not generally for runtime use.
119      */
120     public Generator(WebSocketPolicy policy, ByteBufferPool bufferPool, boolean validating, boolean readOnly)
121     {
122         this.behavior = policy.getBehavior();
123         this.bufferPool = bufferPool;
124         this.validating = validating;
125         this.readOnly = readOnly;
126     }
127 
128     public void assertFrameValid(Frame frame)
129     {
130         if (!validating)
131         {
132             return;
133         }
134 
135         /*
136          * RFC 6455 Section 5.2
137          * 
138          * MUST be 0 unless an extension is negotiated that defines meanings for non-zero values. If a nonzero value is received and none of the negotiated
139          * extensions defines the meaning of such a nonzero value, the receiving endpoint MUST _Fail the WebSocket Connection_.
140          */
141         if (frame.isRsv1() && !isRsv1InUse())
142         {
143             throw new ProtocolException("RSV1 not allowed to be set");
144         }
145 
146         if (frame.isRsv2() && !isRsv2InUse())
147         {
148             throw new ProtocolException("RSV2 not allowed to be set");
149         }
150 
151         if (frame.isRsv3() && !isRsv3InUse())
152         {
153             throw new ProtocolException("RSV3 not allowed to be set");
154         }
155 
156         if (OpCode.isControlFrame(frame.getOpCode()))
157         {
158             /*
159              * RFC 6455 Section 5.5
160              * 
161              * All control frames MUST have a payload length of 125 bytes or less and MUST NOT be fragmented.
162              */
163             if (frame.getPayloadLength() > 125)
164             {
165                 throw new ProtocolException("Invalid control frame payload length");
166             }
167 
168             if (!frame.isFin())
169             {
170                 throw new ProtocolException("Control Frames must be FIN=true");
171             }
172 
173             /*
174              * RFC 6455 Section 5.5.1
175              * 
176              * close frame payload is specially formatted which is checked in CloseInfo
177              */
178             if (frame.getOpCode() == OpCode.CLOSE)
179             {
180 
181                 ByteBuffer payload = frame.getPayload();
182                 if (payload != null)
183                 {
184                     new CloseInfo(payload,true);
185                 }
186             }
187         }
188     }
189 
190     public void configureFromExtensions(List<? extends Extension> exts)
191     {
192         // default
193         flagsInUse = 0x00;
194 
195         // configure from list of extensions in use
196         for (Extension ext : exts)
197         {
198             if (ext.isRsv1User())
199             {
200                 flagsInUse = (byte)(flagsInUse | 0x40);
201             }
202             if (ext.isRsv2User())
203             {
204                 flagsInUse = (byte)(flagsInUse | 0x20);
205             }
206             if (ext.isRsv3User())
207             {
208                 flagsInUse = (byte)(flagsInUse | 0x10);
209             }
210         }
211     }
212 
213     public ByteBuffer generateHeaderBytes(Frame frame)
214     {
215         ByteBuffer buffer = bufferPool.acquire(MAX_HEADER_LENGTH,true);
216         generateHeaderBytes(frame,buffer);
217         return buffer;
218     }
219 
220     public void generateHeaderBytes(Frame frame, ByteBuffer buffer)
221     {
222         int p = BufferUtil.flipToFill(buffer);
223 
224         // we need a framing header
225         assertFrameValid(frame);
226 
227         /*
228          * start the generation process
229          */
230         byte b = 0x00;
231 
232         // Setup fin thru opcode
233         if (frame.isFin())
234         {
235             b |= 0x80; // 1000_0000
236         }
237 
238         // Set the flags
239         if (frame.isRsv1())
240         {
241             b |= 0x40; // 0100_0000
242         }
243         if (frame.isRsv2())
244         {
245             b |= 0x20; // 0010_0000
246         }
247         if (frame.isRsv3())
248         {
249             b |= 0x10; // 0001_0000
250         }
251 
252         // NOTE: using .getOpCode() here, not .getType().getOpCode() for testing reasons
253         byte opcode = frame.getOpCode();
254 
255         if (frame.getOpCode() == OpCode.CONTINUATION)
256         {
257             // Continuations are not the same OPCODE
258             opcode = OpCode.CONTINUATION;
259         }
260 
261         b |= opcode & 0x0F;
262 
263         buffer.put(b);
264 
265         // is masked
266         b = (frame.isMasked()?(byte)0x80:(byte)0x00);
267 
268         // payload lengths
269         int payloadLength = frame.getPayloadLength();
270 
271         /*
272          * if length is over 65535 then its a 7 + 64 bit length
273          */
274         if (payloadLength > 0xFF_FF)
275         {
276             // we have a 64 bit length
277             b |= 0x7F;
278             buffer.put(b); // indicate 8 byte length
279             buffer.put((byte)0); //
280             buffer.put((byte)0); // anything over an
281             buffer.put((byte)0); // int is just
282             buffer.put((byte)0); // insane!
283             buffer.put((byte)((payloadLength >> 24) & 0xFF));
284             buffer.put((byte)((payloadLength >> 16) & 0xFF));
285             buffer.put((byte)((payloadLength >> 8) & 0xFF));
286             buffer.put((byte)(payloadLength & 0xFF));
287         }
288         /*
289          * if payload is greater that 126 we have a 7 + 16 bit length
290          */
291         else if (payloadLength >= 0x7E)
292         {
293             b |= 0x7E;
294             buffer.put(b); // indicate 2 byte length
295             buffer.put((byte)(payloadLength >> 8));
296             buffer.put((byte)(payloadLength & 0xFF));
297         }
298         /*
299          * we have a 7 bit length
300          */
301         else
302         {
303             b |= (payloadLength & 0x7F);
304             buffer.put(b);
305         }
306 
307         // masking key
308         if (frame.isMasked() && !readOnly)
309         {
310             byte[] mask = frame.getMask();
311             buffer.put(mask);
312             int maskInt = 0;
313             for (byte maskByte : mask)
314                 maskInt = (maskInt << 8) + (maskByte & 0xFF);
315 
316             // perform data masking here
317             ByteBuffer payload = frame.getPayload();
318             if ((payload != null) && (payload.remaining() > 0))
319             {
320                 int maskOffset = 0;
321                 int start = payload.position();
322                 int end = payload.limit();
323                 int remaining;
324                 while ((remaining = end - start) > 0)
325                 {
326                     if (remaining >= 4)
327                     {
328                         payload.putInt(start,payload.getInt(start) ^ maskInt);
329                         start += 4;
330                     }
331                     else
332                     {
333                         payload.put(start,(byte)(payload.get(start) ^ mask[maskOffset & 3]));
334                         ++start;
335                         ++maskOffset;
336                     }
337                 }
338             }
339         }
340 
341         BufferUtil.flipToFlush(buffer,p);
342     }
343 
344     /**
345      * Generate the whole frame (header + payload copy) into a single ByteBuffer.
346      * <p>
347      * Note: This is slow, moves lots of memory around. Only use this if you must (such as in unit testing).
348      * 
349      * @param frame
350      *            the frame to generate
351      * @param buf
352      *            the buffer to output the generated frame to
353      */
354     public void generateWholeFrame(Frame frame, ByteBuffer buf)
355     {
356         buf.put(generateHeaderBytes(frame));
357         if (frame.hasPayload())
358         {
359             if (readOnly)
360             {
361                 buf.put(frame.getPayload().slice());
362             }
363             else
364             {
365                 buf.put(frame.getPayload());
366             }
367         }
368     }
369 
370     public ByteBufferPool getBufferPool()
371     {
372         return bufferPool;
373     }
374 
375     public void setRsv1InUse(boolean rsv1InUse)
376     {
377         if (readOnly)
378         {
379             throw new RuntimeException("Not allowed to modify read-only frame");
380         }
381         flagsInUse = (byte)((flagsInUse & 0xBF) | (rsv1InUse?0x40:0x00));
382     }
383 
384     public void setRsv2InUse(boolean rsv2InUse)
385     {
386         if (readOnly)
387         {
388             throw new RuntimeException("Not allowed to modify read-only frame");
389         }
390         flagsInUse = (byte)((flagsInUse & 0xDF) | (rsv2InUse?0x20:0x00));
391     }
392 
393     public void setRsv3InUse(boolean rsv3InUse)
394     {
395         if (readOnly)
396         {
397             throw new RuntimeException("Not allowed to modify read-only frame");
398         }
399         flagsInUse = (byte)((flagsInUse & 0xEF) | (rsv3InUse?0x10:0x00));
400     }
401 
402     public boolean isRsv1InUse()
403     {
404         return (flagsInUse & 0x40) != 0;
405     }
406 
407     public boolean isRsv2InUse()
408     {
409         return (flagsInUse & 0x20) != 0;
410     }
411 
412     public boolean isRsv3InUse()
413     {
414         return (flagsInUse & 0x10) != 0;
415     }
416 
417     @Override
418     public String toString()
419     {
420         StringBuilder builder = new StringBuilder();
421         builder.append("Generator[");
422         builder.append(behavior);
423         if (validating)
424         {
425             builder.append(",validating");
426         }
427         if (isRsv1InUse())
428         {
429             builder.append(",+rsv1");
430         }
431         if (isRsv2InUse())
432         {
433             builder.append(",+rsv2");
434         }
435         if (isRsv3InUse())
436         {
437             builder.append(",+rsv3");
438         }
439         builder.append("]");
440         return builder.toString();
441     }
442 }