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  package org.eclipse.jetty.spdy.parser;
14  
15  import java.nio.ByteBuffer;
16  
17  import org.eclipse.jetty.spdy.api.SPDY;
18  import org.eclipse.jetty.spdy.api.Settings;
19  import org.eclipse.jetty.spdy.frames.SettingsFrame;
20  
21  public class SettingsBodyParser extends ControlFrameBodyParser
22  {
23      private final Settings settings = new Settings();
24      private final ControlFrameParser controlFrameParser;
25      private State state = State.COUNT;
26      private int cursor;
27      private int count;
28      private int idAndFlags;
29      private int value;
30  
31      public SettingsBodyParser(ControlFrameParser controlFrameParser)
32      {
33          this.controlFrameParser = controlFrameParser;
34      }
35  
36      @Override
37      public boolean parse(ByteBuffer buffer)
38      {
39          while (buffer.hasRemaining())
40          {
41              switch (state)
42              {
43                  case COUNT:
44                  {
45                      if (buffer.remaining() >= 4)
46                      {
47                          count = buffer.getInt();
48                          state = State.ID_FLAGS;
49                      }
50                      else
51                      {
52                          state = State.COUNT_BYTES;
53                          cursor = 4;
54                      }
55                      break;
56                  }
57                  case COUNT_BYTES:
58                  {
59                      byte currByte = buffer.get();
60                      --cursor;
61                      count += (currByte & 0xFF) << 8 * cursor;
62                      if (cursor == 0)
63                          state = State.ID_FLAGS;
64                      break;
65                  }
66                  case ID_FLAGS:
67                  {
68                      if (buffer.remaining() >= 4)
69                      {
70                          idAndFlags = convertIdAndFlags(controlFrameParser.getVersion(), buffer.getInt());
71                          state = State.VALUE;
72                      }
73                      else
74                      {
75                          state = State.ID_FLAGS_BYTES;
76                          cursor = 4;
77                      }
78                      break;
79                  }
80                  case ID_FLAGS_BYTES:
81                  {
82                      byte currByte = buffer.get();
83                      --cursor;
84                      value += (currByte & 0xFF) << 8 * cursor;
85                      if (cursor == 0)
86                      {
87                          idAndFlags = convertIdAndFlags(controlFrameParser.getVersion(), value);
88                          state = State.VALUE;
89                      }
90                      break;
91                  }
92                  case VALUE:
93                  {
94                      if (buffer.remaining() >= 4)
95                      {
96                          value = buffer.getInt();
97                          if (onPair())
98                              return true;
99                      }
100                     else
101                     {
102                         state = State.VALUE_BYTES;
103                         cursor = 4;
104                         value = 0;
105                     }
106                     break;
107                 }
108                 case VALUE_BYTES:
109                 {
110                     byte currByte = buffer.get();
111                     --cursor;
112                     value += (currByte & 0xFF) << 8 * cursor;
113                     if (cursor == 0)
114                     {
115                         if (onPair())
116                             return true;
117                     }
118                     break;
119                 }
120                 default:
121                 {
122                     throw new IllegalStateException();
123                 }
124             }
125         }
126         return false;
127     }
128 
129     private int convertIdAndFlags(short version, int idAndFlags)
130     {
131         switch (version)
132         {
133             case SPDY.V2:
134             {
135                 // A bug in the Chromium implementation forces v2 to have
136                 // 3 ID bytes little endian + 1 byte of flags
137                 // Here we normalize this to conform with v3, which is
138                 // 1 bytes of flag + 3 ID bytes big endian
139                 int result = (idAndFlags & 0x00_00_00_FF) << 24;
140                 result += (idAndFlags & 0x00_00_FF_00) << 8;
141                 result += (idAndFlags & 0x00_FF_00_00) >>> 8;
142                 result += (idAndFlags & 0xFF_00_00_00) >>> 24;
143                 return result;
144             }
145             case SPDY.V3:
146             {
147                 return idAndFlags;
148             }
149             default:
150             {
151                 throw new IllegalStateException();
152             }
153         }
154     }
155 
156     private boolean onPair()
157     {
158         int id = idAndFlags & 0x00_FF_FF_FF;
159         byte flags = (byte)((idAndFlags & 0xFF_00_00_00) >>> 24);
160         settings.put(new Settings.Setting(Settings.ID.from(id), Settings.Flag.from(flags), value));
161         state = State.ID_FLAGS;
162         idAndFlags = 0;
163         value = 0;
164         --count;
165         if (count == 0)
166         {
167             onSettings();
168             return true;
169         }
170         return false;
171     }
172 
173     private void onSettings()
174     {
175         SettingsFrame frame = new SettingsFrame(controlFrameParser.getVersion(), controlFrameParser.getFlags(), new Settings(settings, true));
176         controlFrameParser.onControlFrame(frame);
177         reset();
178     }
179 
180     private void reset()
181     {
182         settings.clear();
183         state = State.COUNT;
184         cursor = 0;
185         count = 0;
186         idAndFlags = 0;
187         value = 0;
188     }
189 
190     private enum State
191     {
192         COUNT, COUNT_BYTES, ID_FLAGS, ID_FLAGS_BYTES, VALUE, VALUE_BYTES
193     }
194 }