View Javadoc

1   //========================================================================
2   //Copyright 2011-2012 Mort Bay Consulting Pty. Ltd.
3   //------------------------------------------------------------------------
4   //All rights reserved. This program and the accompanying materials
5   //are made available under the terms of the Eclipse Public License v1.0
6   //and Apache License v2.0 which accompanies this distribution.
7   //The Eclipse Public License is available at
8   //http://www.eclipse.org/legal/epl-v10.html
9   //The Apache License v2.0 is available at
10  //http://www.opensource.org/licenses/apache2.0.php
11  //You may elect to redistribute this code under either of these licenses.
12  //========================================================================
13  
14  package org.eclipse.jetty.spdy.parser;
15  
16  import java.nio.ByteBuffer;
17  import java.util.EnumMap;
18  
19  import org.eclipse.jetty.spdy.CompressionFactory;
20  import org.eclipse.jetty.spdy.frames.ControlFrame;
21  import org.eclipse.jetty.spdy.frames.ControlFrameType;
22  
23  public abstract class ControlFrameParser
24  {
25      private final EnumMap<ControlFrameType, ControlFrameBodyParser> parsers = new EnumMap<>(ControlFrameType.class);
26      private final ControlFrameBodyParser unknownParser = new UnknownControlFrameBodyParser(this);
27      private State state = State.VERSION;
28      private int cursor;
29      private short version;
30      private short type;
31      private byte flags;
32      private int length;
33      private ControlFrameBodyParser parser;
34  
35      public ControlFrameParser(CompressionFactory.Decompressor decompressor)
36      {
37          parsers.put(ControlFrameType.SYN_STREAM, new SynStreamBodyParser(decompressor, this));
38          parsers.put(ControlFrameType.SYN_REPLY, new SynReplyBodyParser(decompressor, this));
39          parsers.put(ControlFrameType.RST_STREAM, new RstStreamBodyParser(this));
40          parsers.put(ControlFrameType.SETTINGS, new SettingsBodyParser(this));
41          parsers.put(ControlFrameType.NOOP, new NoOpBodyParser(this));
42          parsers.put(ControlFrameType.PING, new PingBodyParser(this));
43          parsers.put(ControlFrameType.GO_AWAY, new GoAwayBodyParser(this));
44          parsers.put(ControlFrameType.HEADERS, new HeadersBodyParser(decompressor, this));
45          parsers.put(ControlFrameType.WINDOW_UPDATE, new WindowUpdateBodyParser(this));
46          parsers.put(ControlFrameType.CREDENTIAL, new CredentialBodyParser(this));
47      }
48  
49      public short getVersion()
50      {
51          return version;
52      }
53  
54      public byte getFlags()
55      {
56          return flags;
57      }
58  
59      public int getLength()
60      {
61          return length;
62      }
63  
64      public boolean parse(ByteBuffer buffer)
65      {
66          while (buffer.hasRemaining())
67          {
68              switch (state)
69              {
70                  case VERSION:
71                  {
72                      if (buffer.remaining() >= 2)
73                      {
74                          version = (short)(buffer.getShort() & 0x7F_FF);
75                          state = State.TYPE;
76                      }
77                      else
78                      {
79                          state = State.VERSION_BYTES;
80                          cursor = 2;
81                      }
82                      break;
83                  }
84                  case VERSION_BYTES:
85                  {
86                      byte currByte = buffer.get();
87                      --cursor;
88                      version += (currByte & 0xFF) << 8 * cursor;
89                      if (cursor == 0)
90                      {
91                          version &= 0x7F_FF;
92                          state = State.TYPE;
93                      }
94                      break;
95                  }
96                  case TYPE:
97                  {
98                      if (buffer.remaining() >= 2)
99                      {
100                         type = buffer.getShort();
101                         state = State.FLAGS;
102                     }
103                     else
104                     {
105                         state = State.TYPE_BYTES;
106                         cursor = 2;
107                     }
108                     break;
109                 }
110                 case TYPE_BYTES:
111                 {
112                     byte currByte = buffer.get();
113                     --cursor;
114                     type += (currByte & 0xFF) << 8 * cursor;
115                     if (cursor == 0)
116                         state = State.FLAGS;
117                     break;
118                 }
119                 case FLAGS:
120                 {
121                     flags = buffer.get();
122                     cursor = 3;
123                     state = State.LENGTH;
124                     break;
125                 }
126                 case LENGTH:
127                 {
128                     byte currByte = buffer.get();
129                     --cursor;
130                     length += (currByte & 0xFF) << 8 * cursor;
131                     if (cursor > 0)
132                         break;
133 
134                     ControlFrameType controlFrameType = ControlFrameType.from(type);
135 
136                     // SPEC v3, 2.2.1: unrecognized control frames must be ignored
137                     if (controlFrameType == null)
138                         parser = unknownParser;
139                     else
140                         parser = parsers.get(controlFrameType);
141 
142                     state = State.BODY;
143 
144                     // We have to let it fall through the next switch:
145                     // the NOOP frame has no body and we cannot break
146                     // because the buffer may be consumed and we will
147                     // never enter the BODY case.
148                 }
149                 case BODY:
150                 {
151                     if (parser.parse(buffer))
152                     {
153                         reset();
154                         return true;
155                     }
156                     break;
157                 }
158                 default:
159                 {
160                     throw new IllegalStateException();
161                 }
162             }
163         }
164         return false;
165     }
166 
167     private void reset()
168     {
169         state = State.VERSION;
170         cursor = 0;
171         version = 0;
172         type = 0;
173         flags = 0;
174         length = 0;
175         parser = null;
176     }
177 
178     protected abstract void onControlFrame(ControlFrame frame);
179 
180     private enum State
181     {
182         VERSION, VERSION_BYTES, TYPE, TYPE_BYTES, FLAGS, LENGTH, BODY
183     }
184 }