View Javadoc

1   /*
2    * Copyright (c) 2012 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.eclipse.jetty.spdy.parser;
18  
19  import java.nio.ByteBuffer;
20  import java.util.EnumMap;
21  
22  import org.eclipse.jetty.spdy.CompressionFactory;
23  import org.eclipse.jetty.spdy.frames.ControlFrame;
24  import org.eclipse.jetty.spdy.frames.ControlFrameType;
25  
26  public abstract class ControlFrameParser
27  {
28      private final EnumMap<ControlFrameType, ControlFrameBodyParser> parsers = new EnumMap<>(ControlFrameType.class);
29      private final ControlFrameBodyParser unknownParser = new UnknownControlFrameBodyParser(this);
30      private State state = State.VERSION;
31      private int cursor;
32      private short version;
33      private short type;
34      private byte flags;
35      private int length;
36      private ControlFrameBodyParser parser;
37  
38      public ControlFrameParser(CompressionFactory.Decompressor decompressor)
39      {
40          parsers.put(ControlFrameType.SYN_STREAM, new SynStreamBodyParser(decompressor, this));
41          parsers.put(ControlFrameType.SYN_REPLY, new SynReplyBodyParser(decompressor, this));
42          parsers.put(ControlFrameType.RST_STREAM, new RstStreamBodyParser(this));
43          parsers.put(ControlFrameType.SETTINGS, new SettingsBodyParser(this));
44          parsers.put(ControlFrameType.NOOP, new NoOpBodyParser(this));
45          parsers.put(ControlFrameType.PING, new PingBodyParser(this));
46          parsers.put(ControlFrameType.GO_AWAY, new GoAwayBodyParser(this));
47          parsers.put(ControlFrameType.HEADERS, new HeadersBodyParser(decompressor, this));
48          parsers.put(ControlFrameType.WINDOW_UPDATE, new WindowUpdateBodyParser(this));
49      }
50  
51      public short getVersion()
52      {
53          return version;
54      }
55  
56      public byte getFlags()
57      {
58          return flags;
59      }
60  
61      public int getLength()
62      {
63          return length;
64      }
65  
66      public boolean parse(ByteBuffer buffer)
67      {
68          while (buffer.hasRemaining())
69          {
70              switch (state)
71              {
72                  case VERSION:
73                  {
74                      if (buffer.remaining() >= 2)
75                      {
76                          version = (short)(buffer.getShort() & 0x7F_FF);
77                          state = State.TYPE;
78                      }
79                      else
80                      {
81                          state = State.VERSION_BYTES;
82                          cursor = 2;
83                      }
84                      break;
85                  }
86                  case VERSION_BYTES:
87                  {
88                      byte currByte = buffer.get();
89                      --cursor;
90                      version += (currByte & 0xFF) << 8 * cursor;
91                      if (cursor == 0)
92                      {
93                          version &= 0x7F_FF;
94                          state = State.TYPE;
95                      }
96                      break;
97                  }
98                  case TYPE:
99                  {
100                     if (buffer.remaining() >= 2)
101                     {
102                         type = buffer.getShort();
103                         state = State.FLAGS;
104                     }
105                     else
106                     {
107                         state = State.TYPE_BYTES;
108                         cursor = 2;
109                     }
110                     break;
111                 }
112                 case TYPE_BYTES:
113                 {
114                     byte currByte = buffer.get();
115                     --cursor;
116                     type += (currByte & 0xFF) << 8 * cursor;
117                     if (cursor == 0)
118                         state = State.FLAGS;
119                     break;
120                 }
121                 case FLAGS:
122                 {
123                     flags = buffer.get();
124                     cursor = 3;
125                     state = State.LENGTH;
126                     break;
127                 }
128                 case LENGTH:
129                 {
130                     byte currByte = buffer.get();
131                     --cursor;
132                     length += (currByte & 0xFF) << 8 * cursor;
133                     if (cursor > 0)
134                         break;
135 
136                     ControlFrameType controlFrameType = ControlFrameType.from(type);
137 
138                     // SPEC v3, 2.2.1: unrecognized control frames must be ignored
139                     if (controlFrameType == null)
140                         parser = unknownParser;
141                     else
142                         parser = parsers.get(controlFrameType);
143 
144                     state = State.BODY;
145 
146                     // We have to let it fall through the next switch:
147                     // the NOOP frame has no body and we cannot break
148                     // because the buffer may be consumed and we will
149                     // never enter the BODY case.
150                 }
151                 case BODY:
152                 {
153                     if (parser.parse(buffer))
154                     {
155                         reset();
156                         return true;
157                     }
158                     break;
159                 }
160                 default:
161                 {
162                     throw new IllegalStateException();
163                 }
164             }
165         }
166         return false;
167     }
168 
169     private void reset()
170     {
171         state = State.VERSION;
172         cursor = 0;
173         version = 0;
174         type = 0;
175         flags = 0;
176         length = 0;
177         parser = null;
178     }
179 
180     protected abstract void onControlFrame(ControlFrame frame);
181 
182     private enum State
183     {
184         VERSION, VERSION_BYTES, TYPE, TYPE_BYTES, FLAGS, LENGTH, BODY
185     }
186 }