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.spdy.parser;
20  
21  import java.nio.ByteBuffer;
22  import java.util.EnumMap;
23  
24  import org.eclipse.jetty.spdy.CompressionFactory;
25  import org.eclipse.jetty.spdy.frames.ControlFrame;
26  import org.eclipse.jetty.spdy.frames.ControlFrameType;
27  
28  public abstract class ControlFrameParser
29  {
30      private final EnumMap<ControlFrameType, ControlFrameBodyParser> parsers = new EnumMap<>(ControlFrameType.class);
31      private final ControlFrameBodyParser unknownParser = new UnknownControlFrameBodyParser(this);
32      private State state = State.VERSION;
33      private int cursor;
34      private short version;
35      private short type;
36      private byte flags;
37      private int length;
38      private ControlFrameBodyParser bodyParser;
39      private int bytesToSkip = 0;
40  
41      public ControlFrameParser(CompressionFactory.Decompressor decompressor)
42      {
43          parsers.put(ControlFrameType.SYN_STREAM, new SynStreamBodyParser(decompressor, this));
44          parsers.put(ControlFrameType.SYN_REPLY, new SynReplyBodyParser(decompressor, this));
45          parsers.put(ControlFrameType.RST_STREAM, new RstStreamBodyParser(this));
46          parsers.put(ControlFrameType.SETTINGS, new SettingsBodyParser(this));
47          parsers.put(ControlFrameType.NOOP, new NoOpBodyParser(this));
48          parsers.put(ControlFrameType.PING, new PingBodyParser(this));
49          parsers.put(ControlFrameType.GO_AWAY, new GoAwayBodyParser(this));
50          parsers.put(ControlFrameType.HEADERS, new HeadersBodyParser(decompressor, this));
51          parsers.put(ControlFrameType.WINDOW_UPDATE, new WindowUpdateBodyParser(this));
52          parsers.put(ControlFrameType.CREDENTIAL, new CredentialBodyParser(this));
53      }
54  
55      public short getVersion()
56      {
57          return version;
58      }
59  
60      public byte getFlags()
61      {
62          return flags;
63      }
64  
65      public int getLength()
66      {
67          return length;
68      }
69  
70      public void skip(int bytesToSkip)
71      {
72          state = State.SKIP;
73          this.bytesToSkip = bytesToSkip;
74      }
75  
76      public boolean parse(ByteBuffer buffer)
77      {
78          while (buffer.hasRemaining())
79          {
80              switch (state)
81              {
82                  case VERSION:
83                  {
84                      if (buffer.remaining() >= 2)
85                      {
86                          version = (short)(buffer.getShort() & 0x7F_FF);
87                          state = State.TYPE;
88                      }
89                      else
90                      {
91                          state = State.VERSION_BYTES;
92                          cursor = 2;
93                      }
94                      break;
95                  }
96                  case VERSION_BYTES:
97                  {
98                      byte currByte = buffer.get();
99                      --cursor;
100                     version += (currByte & 0xFF) << 8 * cursor;
101                     if (cursor == 0)
102                     {
103                         version &= 0x7F_FF;
104                         state = State.TYPE;
105                     }
106                     break;
107                 }
108                 case TYPE:
109                 {
110                     if (buffer.remaining() >= 2)
111                     {
112                         type = buffer.getShort();
113                         state = State.FLAGS;
114                     }
115                     else
116                     {
117                         state = State.TYPE_BYTES;
118                         cursor = 2;
119                     }
120                     break;
121                 }
122                 case TYPE_BYTES:
123                 {
124                     byte currByte = buffer.get();
125                     --cursor;
126                     type += (currByte & 0xFF) << 8 * cursor;
127                     if (cursor == 0)
128                         state = State.FLAGS;
129                     break;
130                 }
131                 case FLAGS:
132                 {
133                     flags = buffer.get();
134                     cursor = 3;
135                     state = State.LENGTH;
136                     break;
137                 }
138                 case LENGTH:
139                 {
140                     byte currByte = buffer.get();
141                     --cursor;
142                     length += (currByte & 0xFF) << 8 * cursor;
143                     if (cursor > 0)
144                         break;
145 
146                     ControlFrameType controlFrameType = ControlFrameType.from(type);
147 
148                     // SPEC v3, 2.2.1: unrecognized control frames must be ignored
149                     if (controlFrameType == null)
150                         bodyParser = unknownParser;
151                     else
152                         bodyParser = parsers.get(controlFrameType);
153 
154                     state = State.BODY;
155 
156                     // We have to let it fall through the next switch:
157                     // the NOOP frame has no body and we cannot break
158                     // because the buffer may be consumed and we will
159                     // never enter the BODY case.
160                 }
161                 case BODY:
162                 {
163                     if (bodyParser.parse(buffer))
164                     {
165                         reset();
166                         return true;
167                     }
168                     break;
169                 }
170                 case SKIP:
171                 {
172                     int remaining = buffer.remaining();
173                     if (remaining >= bytesToSkip)
174                     {
175                         buffer.position(buffer.position() + bytesToSkip);
176                         reset();
177                         return true;
178                     }
179                     else
180                     {
181                         buffer.position(buffer.limit());
182                         bytesToSkip = bytesToSkip - remaining;
183                         return false;
184                     }
185                 }
186                 default:
187                 {
188                     throw new IllegalStateException();
189                 }
190             }
191         }
192         return false;
193     }
194 
195     void reset()
196     {
197         state = State.VERSION;
198         cursor = 0;
199         version = 0;
200         type = 0;
201         flags = 0;
202         length = 0;
203         bodyParser = null;
204         bytesToSkip = 0;
205     }
206 
207     protected abstract void onControlFrame(ControlFrame frame);
208 
209     private enum State
210     {
211         VERSION, VERSION_BYTES, TYPE, TYPE_BYTES, FLAGS, LENGTH, BODY, SKIP
212     }
213 }