View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 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.http2.parser;
20  
21  import java.nio.ByteBuffer;
22  import java.util.HashMap;
23  import java.util.Map;
24  import java.util.concurrent.atomic.AtomicReference;
25  
26  import org.eclipse.jetty.http2.ErrorCode;
27  import org.eclipse.jetty.http2.Flags;
28  import org.eclipse.jetty.http2.frames.SettingsFrame;
29  import org.eclipse.jetty.util.log.Log;
30  import org.eclipse.jetty.util.log.Logger;
31  
32  public class SettingsBodyParser extends BodyParser
33  {
34      private static final Logger LOG = Log.getLogger(SettingsBodyParser.class);
35      private State state = State.PREPARE;
36      private int cursor;
37      private int length;
38      private int settingId;
39      private int settingValue;
40      private Map<Integer, Integer> settings;
41  
42      public SettingsBodyParser(HeaderParser headerParser, Parser.Listener listener)
43      {
44          super(headerParser, listener);
45      }
46  
47      protected void reset()
48      {
49          state = State.PREPARE;
50          cursor = 0;
51          length = 0;
52          settingId = 0;
53          settingValue = 0;
54          settings = null;
55      }
56  
57      @Override
58      protected void emptyBody(ByteBuffer buffer)
59      {
60          onSettings(new HashMap<>());
61      }
62  
63      @Override
64      public boolean parse(ByteBuffer buffer)
65      {
66          while (buffer.hasRemaining())
67          {
68              switch (state)
69              {
70                  case PREPARE:
71                  {
72                      // SPEC: wrong streamId is treated as connection error.
73                      if (getStreamId() != 0)
74                          return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_settings_frame");
75                      length = getBodyLength();
76                      settings = new HashMap<>();
77                      state = State.SETTING_ID;
78                      break;
79                  }
80                  case SETTING_ID:
81                  {
82                      if (buffer.remaining() >= 2)
83                      {
84                          settingId = buffer.getShort() & 0xFF_FF;
85                          state = State.SETTING_VALUE;
86                          length -= 2;
87                          if (length <= 0)
88                              return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_settings_frame");
89                      }
90                      else
91                      {
92                          cursor = 2;
93                          settingId = 0;
94                          state = State.SETTING_ID_BYTES;
95                      }
96                      break;
97                  }
98                  case SETTING_ID_BYTES:
99                  {
100                     int currByte = buffer.get() & 0xFF;
101                     --cursor;
102                     settingId += currByte << (8 * cursor);
103                     --length;
104                     if (length <= 0)
105                         return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_settings_frame");
106                     if (cursor == 0)
107                     {
108                         state = State.SETTING_VALUE;
109                     }
110                     break;
111                 }
112                 case SETTING_VALUE:
113                 {
114                     if (buffer.remaining() >= 4)
115                     {
116                         settingValue = buffer.getInt();
117                         if (LOG.isDebugEnabled())
118                             LOG.debug(String.format("setting %d=%d",settingId, settingValue));
119                         settings.put(settingId, settingValue);
120                         state = State.SETTING_ID;
121                         length -= 4;
122                         if (length == 0)
123                             return onSettings(settings);
124                     }
125                     else
126                     {
127                         cursor = 4;
128                         settingValue = 0;
129                         state = State.SETTING_VALUE_BYTES;
130                     }
131                     break;
132                 }
133                 case SETTING_VALUE_BYTES:
134                 {
135                     int currByte = buffer.get() & 0xFF;
136                     --cursor;
137                     settingValue += currByte << (8 * cursor);
138                     --length;
139                     if (cursor > 0 && length <= 0)
140                         return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_settings_frame");
141                     if (cursor == 0)
142                     {
143                         if (LOG.isDebugEnabled())
144                             LOG.debug(String.format("setting %d=%d",settingId, settingValue));
145                         settings.put(settingId, settingValue);
146                         state = State.SETTING_ID;
147                         if (length == 0)
148                             return onSettings(settings);
149                     }
150                     break;
151                 }
152                 default:
153                 {
154                     throw new IllegalStateException();
155                 }
156             }
157         }
158         return false;
159     }
160 
161     protected boolean onSettings(Map<Integer, Integer> settings)
162     {
163         SettingsFrame frame = new SettingsFrame(settings, hasFlag(Flags.ACK));
164         reset();
165         notifySettings(frame);
166         return true;
167     }
168 
169     public static SettingsFrame parseBody(final ByteBuffer buffer)
170     {
171         final int bodyLength = buffer.remaining();
172         final AtomicReference<SettingsFrame> frameRef = new AtomicReference<>();
173         SettingsBodyParser parser = new SettingsBodyParser(null, null)
174         {
175             @Override
176             protected int getStreamId()
177             {
178                 return 0;
179             }
180 
181             @Override
182             protected int getBodyLength()
183             {
184                 return bodyLength;
185             }
186 
187             @Override
188             protected boolean onSettings(Map<Integer, Integer> settings)
189             {
190                 frameRef.set(new SettingsFrame(settings, false));
191                 return true;
192             }
193 
194             @Override
195             protected boolean connectionFailure(ByteBuffer buffer, int error, String reason)
196             {
197                 frameRef.set(null);
198                 return false;
199             }
200         };
201         if (bodyLength == 0)
202             parser.emptyBody(buffer);
203         else
204             parser.parse(buffer);
205         return frameRef.get();
206     }
207 
208     private enum State
209     {
210         PREPARE, SETTING_ID, SETTING_ID_BYTES, SETTING_VALUE, SETTING_VALUE_BYTES
211     }
212 }