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.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.MessageTooLargeException;
29  import org.eclipse.jetty.websocket.api.ProtocolException;
30  import org.eclipse.jetty.websocket.api.WebSocketBehavior;
31  import org.eclipse.jetty.websocket.api.WebSocketException;
32  import org.eclipse.jetty.websocket.api.WebSocketPolicy;
33  import org.eclipse.jetty.websocket.api.extensions.Extension;
34  import org.eclipse.jetty.websocket.api.extensions.Frame;
35  import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
36  import org.eclipse.jetty.websocket.common.io.payload.CloseReasonValidator;
37  import org.eclipse.jetty.websocket.common.io.payload.DeMaskProcessor;
38  import org.eclipse.jetty.websocket.common.io.payload.NoOpValidator;
39  import org.eclipse.jetty.websocket.common.io.payload.PayloadProcessor;
40  import org.eclipse.jetty.websocket.common.io.payload.UTF8Validator;
41  
42  /**
43   * Parsing of a frames in WebSocket land.
44   */
45  public class Parser
46  {
47      private enum State
48      {
49          START,
50          FINOP,
51          PAYLOAD_LEN,
52          PAYLOAD_LEN_BYTES,
53          MASK,
54          MASK_BYTES,
55          PAYLOAD
56      }
57  
58      private static final Logger LOG = Log.getLogger(Parser.class);
59      private final WebSocketPolicy policy;
60      private final ByteBufferPool bufferPool;
61  
62      // State specific
63      private State state = State.START;
64      private int cursor = 0;
65      // Frame
66      private WebSocketFrame frame;
67      private Frame priorDataFrame;
68      private byte lastDataOpcode;
69      // payload specific
70      private ByteBuffer payload;
71      private int payloadLength;
72      private PayloadProcessor maskProcessor = new DeMaskProcessor();
73      private PayloadProcessor strictnessProcessor;
74  
75      /** Is there an extension using RSV1 */
76      private boolean rsv1InUse = false;
77      /** Is there an extension using RSV2 */
78      private boolean rsv2InUse = false;
79      /** Is there an extension using RSV3 */
80      private boolean rsv3InUse = false;
81      /** Is there an extension that processes invalid UTF8 text messages (such as compressed content) */
82      private boolean isTextFrameValidated = true;
83  
84      private IncomingFrames incomingFramesHandler;
85  
86      public Parser(WebSocketPolicy wspolicy, ByteBufferPool bufferPool)
87      {
88          this.bufferPool = bufferPool;
89          this.policy = wspolicy;
90      }
91  
92      private void assertSanePayloadLength(long len)
93      {
94          LOG.debug("Payload Length: " + len);
95          // Since we use ByteBuffer so often, having lengths over Integer.MAX_VALUE is really impossible.
96          if (len > Integer.MAX_VALUE)
97          {
98              // OMG! Sanity Check! DO NOT WANT! Won't anyone think of the memory!
99              throw new MessageTooLargeException("[int-sane!] cannot handle payload lengths larger than " + Integer.MAX_VALUE);
100         }
101         policy.assertValidMessageSize((int)len);
102 
103         switch (frame.getOpCode())
104         {
105             case OpCode.CLOSE:
106                 if (len == 1)
107                 {
108                     throw new ProtocolException("Invalid close frame payload length, [" + payloadLength + "]");
109                 }
110                 // fall thru
111             case OpCode.PING:
112             case OpCode.PONG:
113                 if (len > WebSocketFrame.MAX_CONTROL_PAYLOAD)
114                 {
115                     throw new ProtocolException("Invalid control frame payload length, [" + payloadLength + "] cannot exceed ["
116                             + WebSocketFrame.MAX_CONTROL_PAYLOAD + "]");
117                 }
118                 break;
119         }
120     }
121 
122     public void configureFromExtensions(List<? extends Extension> exts)
123     {
124         // default
125         this.rsv1InUse = false;
126         this.rsv2InUse = false;
127         this.rsv3InUse = false;
128         this.isTextFrameValidated = true;
129 
130         // configure from list of extensions in use
131         for (Extension ext : exts)
132         {
133             if (ext.isRsv1User())
134             {
135                 this.rsv1InUse = true;
136             }
137             if (ext.isRsv2User())
138             {
139                 this.rsv2InUse = true;
140             }
141             if (ext.isRsv3User())
142             {
143                 this.rsv3InUse = true;
144             }
145             if (ext.isTextDataDecoder())
146             {
147                 this.isTextFrameValidated = false;
148             }
149         }
150     }
151 
152     public IncomingFrames getIncomingFramesHandler()
153     {
154         return incomingFramesHandler;
155     }
156 
157     public WebSocketPolicy getPolicy()
158     {
159         return policy;
160     }
161 
162     public boolean isRsv1InUse()
163     {
164         return rsv1InUse;
165     }
166 
167     public boolean isRsv2InUse()
168     {
169         return rsv2InUse;
170     }
171 
172     public boolean isRsv3InUse()
173     {
174         return rsv3InUse;
175     }
176 
177     protected void notifyFrame(final Frame f)
178     {
179         if (LOG.isDebugEnabled())
180         {
181             LOG.debug("{} Notify {}",policy.getBehavior(),incomingFramesHandler);
182         }
183 
184         if (policy.getBehavior() == WebSocketBehavior.SERVER)
185         {
186             // Parsing on server?
187             // Then you MUST make sure all incoming frames are masked!
188             if (f.isMasked() == false)
189             {
190                 throw new ProtocolException("Client frames MUST be masked (RFC-6455)");
191             }
192         }
193 
194         if (incomingFramesHandler == null)
195         {
196             return;
197         }
198         try
199         {
200             incomingFramesHandler.incomingFrame(f);
201         }
202         catch (WebSocketException e)
203         {
204             notifyWebSocketException(e);
205         }
206         catch (Throwable t)
207         {
208             LOG.warn(t);
209             notifyWebSocketException(new WebSocketException(t));
210         }
211     }
212 
213     protected void notifyWebSocketException(WebSocketException e)
214     {
215         LOG.warn(e);
216         if (incomingFramesHandler == null)
217         {
218             return;
219         }
220         incomingFramesHandler.incomingError(e);
221     }
222 
223     public synchronized void parse(ByteBuffer buffer)
224     {
225         if (buffer.remaining() <= 0)
226         {
227             return;
228         }
229         try
230         {
231             // TODO: create DebugBuffer
232 
233             // parse through all the frames in the buffer
234             while (parseFrame(buffer))
235             {
236                 LOG.debug("{} Parsed Frame: {}",policy.getBehavior(),frame);
237                 notifyFrame(frame);
238                 if (frame.isDataFrame() && frame.isFin())
239                 {
240                     priorDataFrame = null;
241                 }
242                 else
243                 {
244                     priorDataFrame = frame;
245                 }
246             }
247         }
248         catch (WebSocketException e)
249         {
250             buffer.position(buffer.limit()); // consume remaining
251             this.payload = null; // reset
252             notifyWebSocketException(e);
253         }
254         catch (Throwable t)
255         {
256             buffer.position(buffer.limit()); // consume remaining
257             this.payload = null; // reset
258             notifyWebSocketException(new WebSocketException(t));
259         }
260     }
261 
262     /**
263      * Parse the base framing protocol buffer.
264      * <p>
265      * Note the first byte (fin,rsv1,rsv2,rsv3,opcode) are parsed by the {@link Parser#parse(ByteBuffer)} method
266      * <p>
267      * Not overridable
268      * 
269      * @param buffer
270      *            the buffer to parse from.
271      * @return true if done parsing base framing protocol and ready for parsing of the payload. false if incomplete parsing of base framing protocol.
272      */
273     private boolean parseFrame(ByteBuffer buffer)
274     {
275         if (buffer.remaining() <= 0)
276         {
277             return false;
278         }
279 
280         LOG.debug("{} Parsing {} bytes",policy.getBehavior(),buffer.remaining());
281         while (buffer.hasRemaining())
282         {
283             switch (state)
284             {
285                 case START:
286                 {
287                     if ((frame != null) && (frame.isFin()))
288                     {
289                         frame.reset();
290                     }
291 
292                     state = State.FINOP;
293                     break;
294                 }
295                 case FINOP:
296                 {
297                     // peek at byte
298                     byte b = buffer.get();
299                     boolean fin = ((b & 0x80) != 0);
300                     boolean rsv1 = ((b & 0x40) != 0);
301                     boolean rsv2 = ((b & 0x20) != 0);
302                     boolean rsv3 = ((b & 0x10) != 0);
303                     byte opc = (byte)(b & 0x0F);
304                     byte opcode = opc;
305 
306                     if (!OpCode.isKnown(opcode))
307                     {
308                         throw new ProtocolException("Unknown opcode: " + opc);
309                     }
310 
311                     if (LOG.isDebugEnabled())
312                     {
313                         LOG.debug("OpCode {}, fin={} rsv={}{}{}",OpCode.name(opcode),fin,(rsv1?'1':'.'),(rsv2?'1':'.'),(rsv3?'1':'.'));
314                     }
315 
316                     /*
317                      * RFC 6455 Section 5.2
318                      * 
319                      * 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
320                      * negotiated extensions defines the meaning of such a nonzero value, the receiving endpoint MUST _Fail the WebSocket Connection_.
321                      */
322                     if (!rsv1InUse && rsv1)
323                     {
324                         throw new ProtocolException("RSV1 not allowed to be set");
325                     }
326 
327                     if (!rsv2InUse && rsv2)
328                     {
329                         throw new ProtocolException("RSV2 not allowed to be set");
330                     }
331 
332                     if (!rsv3InUse && rsv3)
333                     {
334                         throw new ProtocolException("RSV3 not allowed to be set");
335                     }
336 
337                     boolean isContinuation = false;
338 
339                     switch (opcode)
340                     {
341                         case OpCode.TEXT:
342                             if (isTextFrameValidated)
343                             {
344                                 strictnessProcessor = new UTF8Validator();
345                             }
346                             else
347                             {
348                                 strictnessProcessor = NoOpValidator.INSTANCE;
349                             }
350                             break;
351                         case OpCode.CLOSE:
352                             strictnessProcessor = new CloseReasonValidator();
353                             break;
354                         default:
355                             strictnessProcessor = NoOpValidator.INSTANCE;
356                             break;
357                     }
358 
359                     if (OpCode.isControlFrame(opcode))
360                     {
361                         // control frame validation
362                         if (!fin)
363                         {
364                             throw new ProtocolException("Fragmented Control Frame [" + OpCode.name(opcode) + "]");
365                         }
366                     }
367                     else if (opcode == OpCode.CONTINUATION)
368                     {
369                         isContinuation = true;
370                         // continuation validation
371                         if (priorDataFrame == null)
372                         {
373                             throw new ProtocolException("CONTINUATION frame without prior !FIN");
374                         }
375                         // Be careful to use the original opcode
376                         opcode = lastDataOpcode;
377                     }
378                     else if (OpCode.isDataFrame(opcode))
379                     {
380                         // data validation
381                         if ((priorDataFrame != null) && (!priorDataFrame.isFin()))
382                         {
383                             throw new ProtocolException("Unexpected " + OpCode.name(opcode) + " frame, was expecting CONTINUATION");
384                         }
385                     }
386 
387                     // base framing flags
388                     frame = new WebSocketFrame(opcode);
389                     frame.setFin(fin);
390                     frame.setRsv1(rsv1);
391                     frame.setRsv2(rsv2);
392                     frame.setRsv3(rsv3);
393                     frame.setContinuation(isContinuation);
394 
395                     if (frame.isDataFrame())
396                     {
397                         lastDataOpcode = opcode;
398                     }
399 
400                     state = State.PAYLOAD_LEN;
401                     break;
402                 }
403                 case PAYLOAD_LEN:
404                 {
405                     byte b = buffer.get();
406                     frame.setMasked((b & 0x80) != 0);
407                     payloadLength = (byte)(0x7F & b);
408 
409                     if (payloadLength == 127) // 0x7F
410                     {
411                         // length 8 bytes (extended payload length)
412                         payloadLength = 0;
413                         state = State.PAYLOAD_LEN_BYTES;
414                         cursor = 8;
415                         break; // continue onto next state
416                     }
417                     else if (payloadLength == 126) // 0x7E
418                     {
419                         // length 2 bytes (extended payload length)
420                         payloadLength = 0;
421                         state = State.PAYLOAD_LEN_BYTES;
422                         cursor = 2;
423                         break; // continue onto next state
424                     }
425 
426                     assertSanePayloadLength(payloadLength);
427                     if (frame.isMasked())
428                     {
429                         state = State.MASK;
430                     }
431                     else
432                     {
433                         // special case for empty payloads (no more bytes left in buffer)
434                         if (payloadLength == 0)
435                         {
436                             state = State.START;
437                             return true;
438                         }
439 
440                         maskProcessor.reset(frame);
441                         state = State.PAYLOAD;
442                     }
443 
444                     break;
445                 }
446                 case PAYLOAD_LEN_BYTES:
447                 {
448                     byte b = buffer.get();
449                     --cursor;
450                     payloadLength |= (b & 0xFF) << (8 * cursor);
451                     if (cursor == 0)
452                     {
453                         assertSanePayloadLength(payloadLength);
454                         if (frame.isMasked())
455                         {
456                             state = State.MASK;
457                         }
458                         else
459                         {
460                             // special case for empty payloads (no more bytes left in buffer)
461                             if (payloadLength == 0)
462                             {
463                                 state = State.START;
464                                 return true;
465                             }
466 
467                             maskProcessor.reset(frame);
468                             state = State.PAYLOAD;
469                         }
470                     }
471                     break;
472                 }
473                 case MASK:
474                 {
475                     byte m[] = new byte[4];
476                     frame.setMask(m);
477                     if (buffer.remaining() >= 4)
478                     {
479                         buffer.get(m,0,4);
480                         // special case for empty payloads (no more bytes left in buffer)
481                         if (payloadLength == 0)
482                         {
483                             state = State.START;
484                             return true;
485                         }
486 
487                         maskProcessor.reset(frame);
488                         state = State.PAYLOAD;
489                     }
490                     else
491                     {
492                         state = State.MASK_BYTES;
493                         cursor = 4;
494                     }
495                     break;
496                 }
497                 case MASK_BYTES:
498                 {
499                     byte b = buffer.get();
500                     frame.getMask()[4 - cursor] = b;
501                     --cursor;
502                     if (cursor == 0)
503                     {
504                         // special case for empty payloads (no more bytes left in buffer)
505                         if (payloadLength == 0)
506                         {
507                             state = State.START;
508                             return true;
509                         }
510 
511                         maskProcessor.reset(frame);
512                         state = State.PAYLOAD;
513                     }
514                     break;
515                 }
516                 case PAYLOAD:
517                 {
518                     if (parsePayload(buffer))
519                     {
520                         // special check for close
521                         if (frame.getOpCode() == OpCode.CLOSE)
522                         {
523                             new CloseInfo(frame);
524                         }
525                         state = State.START;
526                         // we have a frame!
527                         return true;
528                     }
529                     break;
530                 }
531             }
532         }
533 
534         return false;
535     }
536 
537     /**
538      * Implementation specific parsing of a payload
539      * 
540      * @param buffer
541      *            the payload buffer
542      * @return true if payload is done reading, false if incomplete
543      */
544     private boolean parsePayload(ByteBuffer buffer)
545     {
546         if (payloadLength == 0)
547         {
548             return true;
549         }
550 
551         while (buffer.hasRemaining())
552         {
553             if (payload == null)
554             {
555                 frame.assertValid();
556                 payload = bufferPool.acquire(payloadLength,false);
557                 BufferUtil.clearToFill(payload);
558             }
559 
560             // Create a small window of the incoming buffer to work with.
561             // this should only show the payload itself, and not any more
562             // bytes that could belong to the start of the next frame.
563             ByteBuffer window = buffer.slice();
564             int bytesExpected = payloadLength - payload.position();
565             int bytesAvailable = buffer.remaining();
566             int windowBytes = Math.min(bytesAvailable,bytesExpected);
567             window.limit(window.position() + windowBytes);
568 
569             if (LOG.isDebugEnabled())
570             {
571                 LOG.debug("Window: {}",BufferUtil.toDetailString(window));
572             }
573 
574             maskProcessor.process(window);
575             strictnessProcessor.process(window);
576             int len = BufferUtil.put(window,payload);
577 
578             buffer.position(buffer.position() + len); // update incoming buffer position
579 
580             if (payload.position() >= payloadLength)
581             {
582                 BufferUtil.flipToFlush(payload,0);
583                 frame.setPayload(payload);
584                 this.payload = null;
585                 return true;
586             }
587         }
588         return false;
589     }
590 
591     public void setIncomingFramesHandler(IncomingFrames incoming)
592     {
593         this.incomingFramesHandler = incoming;
594     }
595 
596     @Override
597     public String toString()
598     {
599         StringBuilder builder = new StringBuilder();
600         builder.append("Parser[");
601         if (incomingFramesHandler == null)
602         {
603             builder.append("NO_HANDLER");
604         }
605         else
606         {
607             builder.append(incomingFramesHandler.getClass().getSimpleName());
608         }
609         builder.append(",s=");
610         builder.append(state);
611         builder.append(",c=");
612         builder.append(cursor);
613         builder.append(",len=");
614         builder.append(payloadLength);
615         builder.append(",f=");
616         builder.append(frame);
617         builder.append("]");
618         return builder.toString();
619     }
620 }