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.List;
23
24 import org.eclipse.jetty.io.ByteBufferPool;
25 import org.eclipse.jetty.util.BufferUtil;
26 import org.eclipse.jetty.util.log.Log;
27 import org.eclipse.jetty.util.log.Logger;
28 import org.eclipse.jetty.websocket.api.ProtocolException;
29 import org.eclipse.jetty.websocket.api.WebSocketBehavior;
30 import org.eclipse.jetty.websocket.api.WebSocketPolicy;
31 import org.eclipse.jetty.websocket.api.extensions.Extension;
32 import org.eclipse.jetty.websocket.api.extensions.Frame;
33
34 /**
35 * Generating a frame in WebSocket land.
36 *
37 * <pre>
38 * 0 1 2 3
39 * 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
40 * +-+-+-+-+-------+-+-------------+-------------------------------+
41 * |F|R|R|R| opcode|M| Payload len | Extended payload length |
42 * |I|S|S|S| (4) |A| (7) | (16/64) |
43 * |N|V|V|V| |S| | (if payload len==126/127) |
44 * | |1|2|3| |K| | |
45 * +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
46 * | Extended payload length continued, if payload len == 127 |
47 * + - - - - - - - - - - - - - - - +-------------------------------+
48 * | |Masking-key, if MASK set to 1 |
49 * +-------------------------------+-------------------------------+
50 * | Masking-key (continued) | Payload Data |
51 * +-------------------------------- - - - - - - - - - - - - - - - +
52 * : Payload Data continued ... :
53 * + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
54 * | Payload Data continued ... |
55 * +---------------------------------------------------------------+
56 * </pre>
57 */
58 public class Generator
59 {
60 private static final Logger LOG = Log.getLogger(Generator.class);
61 /**
62 * The overhead (maximum) for a framing header. Assuming a maximum sized payload with masking key.
63 */
64 public static final int OVERHEAD = 28;
65
66 private final WebSocketBehavior behavior;
67 private final ByteBufferPool bufferPool;
68 private boolean validating;
69
70 /** Is there an extension using RSV1 */
71 private boolean rsv1InUse = false;
72 /** Is there an extension using RSV2 */
73 private boolean rsv2InUse = false;
74 /** Is there an extension using RSV3 */
75 private boolean rsv3InUse = false;
76
77 /**
78 * Construct Generator with provided policy and bufferPool
79 *
80 * @param policy
81 * the policy to use
82 * @param bufferPool
83 * the buffer pool to use
84 */
85 public Generator(WebSocketPolicy policy, ByteBufferPool bufferPool)
86 {
87 this(policy,bufferPool,true);
88 }
89
90 /**
91 * Construct Generator with provided policy and bufferPool
92 *
93 * @param policy
94 * the policy to use
95 * @param bufferPool
96 * the buffer pool to use
97 * @param validating
98 * true to enable RFC frame validation
99 */
100 public Generator(WebSocketPolicy policy, ByteBufferPool bufferPool, boolean validating)
101 {
102 this.behavior = policy.getBehavior();
103 this.bufferPool = bufferPool;
104 this.validating = validating;
105 }
106
107 public void assertFrameValid(Frame frame)
108 {
109 if (!validating)
110 {
111 return;
112 }
113
114 /*
115 * RFC 6455 Section 5.2
116 *
117 * 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
118 * extensions defines the meaning of such a nonzero value, the receiving endpoint MUST _Fail the WebSocket Connection_.
119 */
120 if (!rsv1InUse && frame.isRsv1())
121 {
122 throw new ProtocolException("RSV1 not allowed to be set");
123 }
124
125 if (!rsv2InUse && frame.isRsv2())
126 {
127 throw new ProtocolException("RSV2 not allowed to be set");
128 }
129
130 if (!rsv3InUse && frame.isRsv3())
131 {
132 throw new ProtocolException("RSV3 not allowed to be set");
133 }
134
135 if (frame.getType().isControl())
136 {
137 /*
138 * RFC 6455 Section 5.5
139 *
140 * All control frames MUST have a payload length of 125 bytes or less and MUST NOT be fragmented.
141 */
142 if (frame.getPayloadLength() > 125)
143 {
144 throw new ProtocolException("Invalid control frame payload length");
145 }
146
147 if (!frame.isFin())
148 {
149 throw new ProtocolException("Control Frames must be FIN=true");
150 }
151
152 /*
153 * RFC 6455 Section 5.5.1
154 *
155 * close frame payload is specially formatted which is checked in CloseInfo
156 */
157 if (frame.getType().getOpCode() == OpCode.CLOSE)
158 {
159
160 ByteBuffer payload = frame.getPayload();
161 if (payload != null)
162 {
163 new CloseInfo(payload,true);
164 }
165 }
166 }
167
168 }
169
170 public void configureFromExtensions(List<? extends Extension> exts)
171 {
172 // default
173 this.rsv1InUse = false;
174 this.rsv2InUse = false;
175 this.rsv3InUse = false;
176
177 // configure from list of extensions in use
178 for(Extension ext: exts)
179 {
180 if (ext.isRsv1User())
181 {
182 this.rsv1InUse = true;
183 }
184 if (ext.isRsv2User())
185 {
186 this.rsv2InUse = true;
187 }
188 if (ext.isRsv3User())
189 {
190 this.rsv3InUse = true;
191 }
192 }
193 }
194
195 /**
196 * generate a byte buffer based on the frame being passed in
197 *
198 * bufferSize is determined by the length of the payload + 28 for frame overhead
199 *
200 * @param frame
201 * @return
202 */
203 public synchronized ByteBuffer generate(Frame frame)
204 {
205 int bufferSize = frame.getPayloadLength() + OVERHEAD;
206 return generate(bufferSize,frame);
207 }
208
209 /**
210 * Generate, into a ByteBuffer, no more than bufferSize of contents from the frame. If the frame exceeds the bufferSize, then multiple calls to
211 * {@link #generate(int, WebSocketFrame)} are required to obtain each window of ByteBuffer to complete the frame.
212 */
213 public synchronized ByteBuffer generate(int windowSize, Frame frame)
214 {
215 if (windowSize < OVERHEAD)
216 {
217 throw new IllegalArgumentException("Cannot have windowSize less than " + OVERHEAD);
218 }
219
220 LOG.debug("{} Generate: {} (windowSize {})",behavior,frame,windowSize);
221
222 /*
223 * prepare the byte buffer to put frame into
224 */
225 ByteBuffer buffer = bufferPool.acquire(windowSize,false);
226 BufferUtil.clearToFill(buffer);
227 if (LOG.isDebugEnabled())
228 {
229 LOG.debug("Acquired Buffer (windowSize={}): {}",windowSize,BufferUtil.toDetailString(buffer));
230 }
231 // since the buffer from the pool can exceed the window size, artificially
232 // limit the buffer to the window size.
233 int newlimit = Math.min(buffer.position() + windowSize,buffer.limit());
234 buffer.limit(newlimit);
235 LOG.debug("Buffer limited: {}",buffer);
236
237 if (frame.remaining() == frame.getPayloadLength())
238 {
239 // we need a framing header
240 assertFrameValid(frame);
241
242 /*
243 * start the generation process
244 */
245 byte b;
246
247 // Setup fin thru opcode
248 b = 0x00;
249 if (frame.isFin())
250 {
251 b |= 0x80; // 1000_0000
252 }
253 if (frame.isRsv1())
254 {
255 b |= 0x40; // 0100_0000
256 }
257 if (frame.isRsv2())
258 {
259 b |= 0x20; // 0010_0000
260 }
261 if (frame.isRsv3())
262 {
263 b |= 0x10;
264 }
265
266 // NOTE: using .getOpCode() here, not .getType().getOpCode() for testing reasons
267 byte opcode = frame.getOpCode();
268
269 if (frame.isContinuation())
270 {
271 // Continuations are not the same OPCODE
272 opcode = OpCode.CONTINUATION;
273 }
274
275 b |= opcode & 0x0F;
276
277 buffer.put(b);
278
279 // is masked
280 b = 0x00;
281 b |= (frame.isMasked()?0x80:0x00);
282
283 // payload lengths
284 int payloadLength = frame.getPayloadLength();
285
286 /*
287 * if length is over 65535 then its a 7 + 64 bit length
288 */
289 if (payloadLength > 0xFF_FF)
290 {
291 // we have a 64 bit length
292 b |= 0x7F;
293 buffer.put(b); // indicate 8 byte length
294 buffer.put((byte)0); //
295 buffer.put((byte)0); // anything over an
296 buffer.put((byte)0); // int is just
297 buffer.put((byte)0); // intsane!
298 buffer.put((byte)((payloadLength >> 24) & 0xFF));
299 buffer.put((byte)((payloadLength >> 16) & 0xFF));
300 buffer.put((byte)((payloadLength >> 8) & 0xFF));
301 buffer.put((byte)(payloadLength & 0xFF));
302 }
303 /*
304 * if payload is ge 126 we have a 7 + 16 bit length
305 */
306 else if (payloadLength >= 0x7E)
307 {
308 b |= 0x7E;
309 buffer.put(b); // indicate 2 byte length
310 buffer.put((byte)(payloadLength >> 8));
311 buffer.put((byte)(payloadLength & 0xFF));
312 }
313 /*
314 * we have a 7 bit length
315 */
316 else
317 {
318 b |= (payloadLength & 0x7F);
319 buffer.put(b);
320 }
321
322 // masking key
323 if (frame.isMasked())
324 {
325 buffer.put(frame.getMask());
326 }
327 }
328
329 // copy payload
330 if (frame.hasPayload())
331 {
332 // remember the position
333 int maskingStartPosition = buffer.position();
334
335 // remember the offset within the frame payload (for working with
336 // windowed frames that don't split on 4 byte barriers)
337 int payloadOffset = frame.getPayload().position();
338 int payloadStart = frame.getPayloadStart();
339
340 // put as much as possible into the buffer
341 BufferUtil.put(frame.getPayload(),buffer);
342
343 // mask it if needed
344 if (frame.isMasked())
345 {
346 // move back to remembered position.
347 int size = buffer.position() - maskingStartPosition;
348 byte[] mask = frame.getMask();
349 byte b;
350 int posBuf;
351 int posFrame;
352 for (int i = 0; i < size; i++)
353 {
354 posBuf = i + maskingStartPosition;
355 posFrame = i + (payloadOffset - payloadStart);
356
357 // get raw byte from buffer.
358 b = buffer.get(posBuf);
359
360 // mask, using offset information from frame windowing.
361 b ^= mask[posFrame % 4];
362
363 // Mask each byte by its absolute position in the bytebuffer
364 buffer.put(posBuf,b);
365 }
366 }
367 }
368
369 BufferUtil.flipToFlush(buffer,0);
370 if (LOG.isDebugEnabled())
371 {
372 LOG.debug("Generated Buffer: {}",BufferUtil.toDetailString(buffer));
373 }
374 return buffer;
375 }
376
377 public ByteBufferPool getBufferPool()
378 {
379 return bufferPool;
380 }
381
382 public boolean isRsv1InUse()
383 {
384 return rsv1InUse;
385 }
386
387 public boolean isRsv2InUse()
388 {
389 return rsv2InUse;
390 }
391
392 public boolean isRsv3InUse()
393 {
394 return rsv3InUse;
395 }
396
397 @Override
398 public String toString()
399 {
400 StringBuilder builder = new StringBuilder();
401 builder.append("Generator[");
402 builder.append(behavior);
403 if (validating)
404 {
405 builder.append(",validating");
406 }
407 if (rsv1InUse)
408 {
409 builder.append(",+rsv1");
410 }
411 if (rsv2InUse)
412 {
413 builder.append(",+rsv2");
414 }
415 if (rsv3InUse)
416 {
417 builder.append(",+rsv3");
418 }
419 builder.append("]");
420 return builder.toString();
421 }
422 }