View Javadoc

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