View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2014 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  
67      /** 
68       * Are any flags in use
69       * <p>
70       * 
71       * <pre>
72       *   0100_0000 (0x40) = rsv1
73       *   0010_0000 (0x20) = rsv2
74       *   0001_0000 (0x10) = rsv3
75       * </pre>
76       */
77      private byte flagsInUse=0x00;
78  
79      /**
80       * Construct Generator with provided policy and bufferPool
81       * 
82       * @param policy
83       *            the policy to use
84       * @param bufferPool
85       *            the buffer pool to use
86       */
87      public Generator(WebSocketPolicy policy, ByteBufferPool bufferPool)
88      {
89          this(policy,bufferPool,true);
90      }
91  
92      /**
93       * Construct Generator with provided policy and bufferPool
94       * 
95       * @param policy
96       *            the policy to use
97       * @param bufferPool
98       *            the buffer pool to use
99       * @param validating
100      *            true to enable RFC frame validation
101      */
102     public Generator(WebSocketPolicy policy, ByteBufferPool bufferPool, boolean validating)
103     {
104         this.behavior = policy.getBehavior();
105         this.bufferPool = bufferPool;
106         this.validating = validating;
107     }
108 
109     public void assertFrameValid(Frame frame)
110     {
111         if (!validating)
112         {
113             return;
114         }
115 
116         /*
117          * RFC 6455 Section 5.2
118          * 
119          * 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
120          * extensions defines the meaning of such a nonzero value, the receiving endpoint MUST _Fail the WebSocket Connection_.
121          */
122         if (frame.isRsv1() && !isRsv1InUse())
123         {
124             throw new ProtocolException("RSV1 not allowed to be set");
125         }
126 
127         if (frame.isRsv2() && !isRsv2InUse())
128         {
129             throw new ProtocolException("RSV2 not allowed to be set");
130         }
131 
132         if (frame.isRsv3() && !isRsv3InUse())
133         {
134             throw new ProtocolException("RSV3 not allowed to be set");
135         }
136 
137         if (OpCode.isControlFrame(frame.getOpCode()))
138         {
139             /*
140              * RFC 6455 Section 5.5
141              * 
142              * All control frames MUST have a payload length of 125 bytes or less and MUST NOT be fragmented.
143              */
144             if (frame.getPayloadLength() > 125)
145             {
146                 throw new ProtocolException("Invalid control frame payload length");
147             }
148 
149             if (!frame.isFin())
150             {
151                 throw new ProtocolException("Control Frames must be FIN=true");
152             }
153 
154             /*
155              * RFC 6455 Section 5.5.1
156              * 
157              * close frame payload is specially formatted which is checked in CloseInfo
158              */
159             if (frame.getOpCode() == OpCode.CLOSE)
160             {
161 
162                 ByteBuffer payload = frame.getPayload();
163                 if (payload != null)
164                 {
165                     new CloseInfo(payload,true);
166                 }
167             }
168         }
169     }
170 
171     public void configureFromExtensions(List<? extends Extension> exts)
172     {
173         // default
174         flagsInUse = 0x00;
175 
176         // configure from list of extensions in use
177         for (Extension ext : exts)
178         {
179             if (ext.isRsv1User())
180             {
181                 flagsInUse = (byte)(flagsInUse | 0x40);
182             }
183             if (ext.isRsv2User())
184             {
185                 flagsInUse = (byte)(flagsInUse | 0x20);
186             }
187             if (ext.isRsv3User())
188             {
189                 flagsInUse = (byte)(flagsInUse | 0x10);
190             }
191         }
192     }
193 
194     public ByteBuffer generateHeaderBytes(Frame frame)
195     {
196         ByteBuffer buffer = bufferPool.acquire(MAX_HEADER_LENGTH,true);
197         generateHeaderBytes(frame,buffer);
198         return buffer;
199     }
200 
201     public void generateHeaderBytes(Frame frame, ByteBuffer buffer)
202     {
203         int p=BufferUtil.flipToFill(buffer);
204         
205         // we need a framing header
206         assertFrameValid(frame);
207         
208         /*
209          * start the generation process
210          */
211         byte b = 0x00;
212         
213         // Setup fin thru opcode
214         if (frame.isFin())
215         {
216             b |= 0x80; // 1000_0000
217         }
218         
219         // Set the flags
220         if (frame.isRsv1())
221         {
222             b |= 0x40; // 0100_0000
223         }
224         if (frame.isRsv2())
225         {
226             b |= 0x20; // 0010_0000
227         }
228         if (frame.isRsv3())
229         {
230             b |= 0x10; // 0001_0000
231         }
232         
233         // NOTE: using .getOpCode() here, not .getType().getOpCode() for testing reasons
234         byte opcode = frame.getOpCode();
235 
236         if (frame.getOpCode() == OpCode.CONTINUATION)
237         {
238             // Continuations are not the same OPCODE
239             opcode = OpCode.CONTINUATION;
240         }
241 
242         b |= opcode & 0x0F;
243 
244         buffer.put(b);
245 
246         // is masked
247         b = (frame.isMasked()?(byte)0x80:(byte)0x00);
248 
249         // payload lengths
250         int payloadLength = frame.getPayloadLength();
251 
252         /*
253          * if length is over 65535 then its a 7 + 64 bit length
254          */
255         if (payloadLength > 0xFF_FF)
256         {
257             // we have a 64 bit length
258             b |= 0x7F;
259             buffer.put(b); // indicate 8 byte length
260             buffer.put((byte)0); //
261             buffer.put((byte)0); // anything over an
262             buffer.put((byte)0); // int is just
263             buffer.put((byte)0); // insane!
264             buffer.put((byte)((payloadLength >> 24) & 0xFF));
265             buffer.put((byte)((payloadLength >> 16) & 0xFF));
266             buffer.put((byte)((payloadLength >> 8) & 0xFF));
267             buffer.put((byte)(payloadLength & 0xFF));
268         }
269         /*
270          * if payload is greater that 126 we have a 7 + 16 bit length
271          */
272         else if (payloadLength >= 0x7E )
273         {
274             b |= 0x7E;
275             buffer.put(b); // indicate 2 byte length
276             buffer.put((byte)(payloadLength >> 8));
277             buffer.put((byte)(payloadLength & 0xFF));
278         }
279         /*
280          * we have a 7 bit length
281          */
282         else
283         {
284             b |= (payloadLength & 0x7F);
285             buffer.put(b);
286         }
287 
288         // masking key
289         if (frame.isMasked())
290         {
291             byte[] mask = frame.getMask();
292             buffer.put(mask);
293             int maskInt = 0;
294             for (byte maskByte : mask)
295                 maskInt = (maskInt << 8) + (maskByte & 0xFF);
296 
297             // perform data masking here
298             ByteBuffer payload = frame.getPayload();
299             if ((payload != null) && (payload.remaining() > 0))
300             {
301                 int maskOffset = 0;
302                 int start = payload.position();
303                 int end = payload.limit();
304                 int remaining;
305                 while ((remaining = end - start) > 0)
306                 {
307                     if (remaining >= 4)
308                     {
309                         payload.putInt(start, payload.getInt(start) ^ maskInt);
310                         start += 4;
311                     }
312                     else
313                     {
314                         payload.put(start, (byte)(payload.get(start) ^ mask[maskOffset & 3]));
315                         ++start;
316                         ++maskOffset;
317                     }
318                 }
319             }
320         }
321 
322         BufferUtil.flipToFlush(buffer,p);
323     }
324 
325     /**
326      * Generate the whole frame (header + payload copy) into a single ByteBuffer.
327      * <p>
328      * Note: THIS IS SLOW. Only use this if you must.
329      * 
330      * @param frame
331      *            the frame to generate
332      */
333     public void generateWholeFrame(Frame frame, ByteBuffer buf)
334     {
335         buf.put(generateHeaderBytes(frame));
336         if (frame.hasPayload())
337         {
338             buf.put(frame.getPayload());
339         }
340     }
341 
342     public ByteBufferPool getBufferPool()
343     {
344         return bufferPool;
345     }
346 
347 
348     public void setRsv1InUse(boolean rsv1InUse)
349     {
350         flagsInUse = (byte)((flagsInUse & 0xBF) | (rsv1InUse?0x40:0x00));
351     }
352 
353     public void setRsv2InUse(boolean rsv2InUse)
354     {
355         flagsInUse = (byte)((flagsInUse & 0xDF) | (rsv2InUse?0x20:0x00));
356     }
357 
358     public void setRsv3InUse(boolean rsv3InUse)
359     {
360         flagsInUse = (byte)((flagsInUse & 0xEF) | (rsv3InUse?0x10:0x00));
361     }
362 
363     public boolean isRsv1InUse()
364     {
365         return (flagsInUse & 0x40) != 0;
366     }
367 
368     public boolean isRsv2InUse()
369     {
370         return (flagsInUse & 0x20) != 0;
371     }
372 
373     public boolean isRsv3InUse()
374     {
375         return (flagsInUse & 0x10) != 0;
376     }
377 
378     @Override
379     public String toString()
380     {
381         StringBuilder builder = new StringBuilder();
382         builder.append("Generator[");
383         builder.append(behavior);
384         if (validating)
385         {
386             builder.append(",validating");
387         }
388         if (isRsv1InUse())
389         {
390             builder.append(",+rsv1");
391         }
392         if (isRsv2InUse())
393         {
394             builder.append(",+rsv2");
395         }
396         if (isRsv3InUse())
397         {
398             builder.append(",+rsv3");
399         }
400         builder.append("]");
401         return builder.toString();
402     }
403 }