View Javadoc
1   /*
2    * Copyright 2014 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. 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, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package org.jboss.netty.handler.codec.spdy;
17  
18  import static org.jboss.netty.handler.codec.spdy.SpdyCodecUtil.*;
19  
20  import org.jboss.netty.buffer.ChannelBuffer;
21  import org.jboss.netty.buffer.ChannelBuffers;
22  
23  /**
24   * Decodes {@link ChannelBuffer}s into SPDY Frames.
25   */
26  public class SpdyFrameDecoder {
27  
28      private final int spdyVersion;
29      private final int maxChunkSize;
30  
31      private final SpdyFrameDecoderDelegate delegate;
32  
33      private State state;
34  
35      // SPDY common header fields
36      private byte flags;
37      private int length;
38      private int streamId;
39  
40      private int numSettings;
41  
42      private enum State {
43          READ_COMMON_HEADER,
44          READ_DATA_FRAME,
45          READ_SYN_STREAM_FRAME,
46          READ_SYN_REPLY_FRAME,
47          READ_RST_STREAM_FRAME,
48          READ_SETTINGS_FRAME,
49          READ_SETTING,
50          READ_PING_FRAME,
51          READ_GOAWAY_FRAME,
52          READ_HEADERS_FRAME,
53          READ_WINDOW_UPDATE_FRAME,
54          READ_HEADER_BLOCK,
55          DISCARD_FRAME,
56          FRAME_ERROR
57      }
58  
59      /**
60       * Creates a new instance with the specified {@code version}
61       * and the default {@code maxChunkSize (8192)}.
62       */
63      public SpdyFrameDecoder(SpdyVersion spdyVersion, SpdyFrameDecoderDelegate delegate) {
64          this(spdyVersion, delegate, 8192);
65      }
66  
67      /**
68       * Creates a new instance with the specified parameters.
69       */
70      public SpdyFrameDecoder(SpdyVersion spdyVersion, SpdyFrameDecoderDelegate delegate, int maxChunkSize) {
71          if (spdyVersion == null) {
72              throw new NullPointerException("spdyVersion");
73          }
74          if (delegate == null) {
75              throw new NullPointerException("delegate");
76          }
77          if (maxChunkSize <= 0) {
78              throw new IllegalArgumentException(
79                      "maxChunkSize must be a positive integer: " + maxChunkSize);
80          }
81          this.spdyVersion = spdyVersion.getVersion();
82          this.delegate = delegate;
83          this.maxChunkSize = maxChunkSize;
84          state = State.READ_COMMON_HEADER;
85      }
86  
87      public void decode(ChannelBuffer buffer) {
88          boolean last;
89          int statusCode;
90  
91          while (true) {
92              switch(state) {
93              case READ_COMMON_HEADER:
94                  if (buffer.readableBytes() < SPDY_HEADER_SIZE) {
95                      return;
96                  }
97  
98                  int frameOffset  = buffer.readerIndex();
99                  int flagsOffset  = frameOffset + SPDY_HEADER_FLAGS_OFFSET;
100                 int lengthOffset = frameOffset + SPDY_HEADER_LENGTH_OFFSET;
101                 buffer.skipBytes(SPDY_HEADER_SIZE);
102 
103                 boolean control = (buffer.getByte(frameOffset) & 0x80) != 0;
104 
105                 int version;
106                 int type;
107                 if (control) {
108                     // Decode control frame common header
109                     version = getUnsignedShort(buffer, frameOffset) & 0x7FFF;
110                     type = getUnsignedShort(buffer, frameOffset + SPDY_HEADER_TYPE_OFFSET);
111                     streamId = 0; // Default to session Stream-ID
112                 } else {
113                     // Decode data frame common header
114                     version = spdyVersion; // Default to expected version
115                     type = SPDY_DATA_FRAME;
116                     streamId = getUnsignedInt(buffer, frameOffset);
117                 }
118 
119                 flags  = buffer.getByte(flagsOffset);
120                 length = getUnsignedMedium(buffer, lengthOffset);
121 
122                 // Check version first then validity
123                 if (version != spdyVersion) {
124                   state = State.FRAME_ERROR;
125                   delegate.readFrameError("Invalid SPDY Version");
126                 } else if (!isValidFrameHeader(streamId, type, flags, length)) {
127                     state = State.FRAME_ERROR;
128                     delegate.readFrameError("Invalid Frame Error");
129                 } else {
130                     state = getNextState(type, length);
131                 }
132                 break;
133 
134             case READ_DATA_FRAME:
135                 if (length == 0) {
136                     state = State.READ_COMMON_HEADER;
137                     delegate.readDataFrame(streamId, hasFlag(flags, SPDY_DATA_FLAG_FIN), ChannelBuffers.EMPTY_BUFFER);
138                     break;
139                 }
140 
141                 // Generate data frames that do not exceed maxChunkSize
142                 int dataLength = Math.min(maxChunkSize, length);
143 
144                 // Wait until entire frame is readable
145                 if (buffer.readableBytes() < dataLength) {
146                     return;
147                 }
148 
149                 ChannelBuffer data = buffer.readBytes(dataLength);
150                 length -= dataLength;
151 
152                 if (length == 0) {
153                     state = State.READ_COMMON_HEADER;
154                 }
155 
156                 last = length == 0 && hasFlag(flags, SPDY_DATA_FLAG_FIN);
157 
158                 delegate.readDataFrame(streamId, last, data);
159                 break;
160 
161             case READ_SYN_STREAM_FRAME:
162                 if (buffer.readableBytes() < 10) {
163                     return;
164                 }
165 
166                 int offset = buffer.readerIndex();
167                 streamId = getUnsignedInt(buffer, offset);
168                 int associatedToStreamId = getUnsignedInt(buffer, offset + 4);
169                 byte priority = (byte) (buffer.getByte(offset + 8) >> 5 & 0x07);
170                 last = hasFlag(flags, SPDY_FLAG_FIN);
171                 boolean unidirectional = hasFlag(flags, SPDY_FLAG_UNIDIRECTIONAL);
172                 buffer.skipBytes(10);
173                 length -= 10;
174 
175                 if (streamId == 0) {
176                     state = State.FRAME_ERROR;
177                     delegate.readFrameError("Invalid SYN_STREAM Frame");
178                 } else {
179                     state = State.READ_HEADER_BLOCK;
180                     delegate.readSynStreamFrame(streamId, associatedToStreamId, priority, last, unidirectional);
181                 }
182                 break;
183 
184             case READ_SYN_REPLY_FRAME:
185                 if (buffer.readableBytes() < 4) {
186                     return;
187                 }
188 
189                 streamId = getUnsignedInt(buffer, buffer.readerIndex());
190                 last = hasFlag(flags, SPDY_FLAG_FIN);
191 
192                 buffer.skipBytes(4);
193                 length -= 4;
194 
195                 if (streamId == 0) {
196                     state = State.FRAME_ERROR;
197                     delegate.readFrameError("Invalid SYN_REPLY Frame");
198                 } else {
199                     state = State.READ_HEADER_BLOCK;
200                     delegate.readSynReplyFrame(streamId, last);
201                 }
202                 break;
203 
204             case READ_RST_STREAM_FRAME:
205                 if (buffer.readableBytes() < 8) {
206                     return;
207                 }
208 
209                 streamId = getUnsignedInt(buffer, buffer.readerIndex());
210                 statusCode = getSignedInt(buffer, buffer.readerIndex() + 4);
211                 buffer.skipBytes(8);
212 
213                 if (streamId == 0 || statusCode == 0) {
214                     state = State.FRAME_ERROR;
215                     delegate.readFrameError("Invalid RST_STREAM Frame");
216                 } else {
217                     state = State.READ_COMMON_HEADER;
218                     delegate.readRstStreamFrame(streamId, statusCode);
219                 }
220                 break;
221 
222             case READ_SETTINGS_FRAME:
223                 if (buffer.readableBytes() < 4) {
224                    return;
225                 }
226 
227                 boolean clear = hasFlag(flags, SPDY_SETTINGS_CLEAR);
228 
229                 numSettings = getUnsignedInt(buffer, buffer.readerIndex());
230                 buffer.skipBytes(4);
231                 length -= 4;
232 
233                 // Validate frame length against number of entries. Each ID/Value entry is 8 bytes.
234                 if ((length & 0x07) != 0 || length >> 3 != numSettings) {
235                     state = State.FRAME_ERROR;
236                     delegate.readFrameError("Invalid SETTINGS Frame");
237                 } else {
238                     state = State.READ_SETTING;
239                     delegate.readSettingsFrame(clear);
240                 }
241                 break;
242 
243             case READ_SETTING:
244                 if (numSettings == 0) {
245                     state = State.READ_COMMON_HEADER;
246                     delegate.readSettingsEnd();
247                     break;
248                 }
249 
250                 if (buffer.readableBytes() < 8) {
251                     return;
252                 }
253 
254                 byte settingsFlags = buffer.getByte(buffer.readerIndex());
255                 int id = getUnsignedMedium(buffer, buffer.readerIndex() + 1);
256                 int value = getSignedInt(buffer, buffer.readerIndex() + 4);
257                 boolean persistValue = hasFlag(settingsFlags, SPDY_SETTINGS_PERSIST_VALUE);
258                 boolean persisted = hasFlag(settingsFlags, SPDY_SETTINGS_PERSISTED);
259                 buffer.skipBytes(8);
260 
261                 --numSettings;
262 
263                 delegate.readSetting(id, value, persistValue, persisted);
264                 break;
265 
266             case READ_PING_FRAME:
267                 if (buffer.readableBytes() < 4) {
268                     return;
269                 }
270 
271                 int pingId = getSignedInt(buffer, buffer.readerIndex());
272                 buffer.skipBytes(4);
273 
274                 state = State.READ_COMMON_HEADER;
275                 delegate.readPingFrame(pingId);
276                 break;
277 
278             case READ_GOAWAY_FRAME:
279                 if (buffer.readableBytes() < 8) {
280                     return;
281                 }
282 
283                 int lastGoodStreamId = getUnsignedInt(buffer, buffer.readerIndex());
284                 statusCode = getSignedInt(buffer, buffer.readerIndex() + 4);
285                 buffer.skipBytes(8);
286 
287                 state = State.READ_COMMON_HEADER;
288                 delegate.readGoAwayFrame(lastGoodStreamId, statusCode);
289                 break;
290 
291             case READ_HEADERS_FRAME:
292                 if (buffer.readableBytes() < 4) {
293                     return;
294                 }
295 
296                 streamId = getUnsignedInt(buffer, buffer.readerIndex());
297                 last = hasFlag(flags, SPDY_FLAG_FIN);
298 
299                 buffer.skipBytes(4);
300                 length -= 4;
301 
302                 if (streamId == 0) {
303                     state = State.FRAME_ERROR;
304                     delegate.readFrameError("Invalid HEADERS Frame");
305                 } else {
306                     state = State.READ_HEADER_BLOCK;
307                     delegate.readHeadersFrame(streamId, last);
308                 }
309                 break;
310 
311             case READ_WINDOW_UPDATE_FRAME:
312                 if (buffer.readableBytes() < 8) {
313                     return;
314                 }
315 
316                 streamId = getUnsignedInt(buffer, buffer.readerIndex());
317                 int deltaWindowSize = getUnsignedInt(buffer, buffer.readerIndex() + 4);
318                 buffer.skipBytes(8);
319 
320                 if (deltaWindowSize == 0) {
321                     state = State.FRAME_ERROR;
322                     delegate.readFrameError("Invalid WINDOW_UPDATE Frame");
323                 } else {
324                     state = State.READ_COMMON_HEADER;
325                     delegate.readWindowUpdateFrame(streamId, deltaWindowSize);
326                 }
327                 break;
328 
329             case READ_HEADER_BLOCK:
330                 if (length == 0) {
331                     state = State.READ_COMMON_HEADER;
332                     delegate.readHeaderBlockEnd();
333                     break;
334                 }
335 
336                 if (!buffer.readable()) {
337                     return;
338                 }
339 
340                 int compressedBytes = Math.min(buffer.readableBytes(), length);
341                 ChannelBuffer headerBlock = buffer.readBytes(compressedBytes);
342                 length -= compressedBytes;
343 
344                 delegate.readHeaderBlock(headerBlock);
345                 break;
346 
347             case DISCARD_FRAME:
348                 int numBytes = Math.min(buffer.readableBytes(), length);
349                 buffer.skipBytes(numBytes);
350                 length -= numBytes;
351                 if (length == 0) {
352                     state = State.READ_COMMON_HEADER;
353                     break;
354                 }
355                 return;
356 
357             case FRAME_ERROR:
358                 buffer.skipBytes(buffer.readableBytes());
359                 return;
360 
361             default:
362                 throw new Error("Shouldn't reach here.");
363             }
364         }
365     }
366 
367     private static boolean hasFlag(byte flags, byte flag) {
368         return (flags & flag) != 0;
369     }
370 
371     private static State getNextState(int type, int length) {
372         switch (type) {
373         case SPDY_DATA_FRAME:
374             return State.READ_DATA_FRAME;
375 
376         case SPDY_SYN_STREAM_FRAME:
377             return State.READ_SYN_STREAM_FRAME;
378 
379         case SPDY_SYN_REPLY_FRAME:
380             return State.READ_SYN_REPLY_FRAME;
381 
382         case SPDY_RST_STREAM_FRAME:
383             return State.READ_RST_STREAM_FRAME;
384 
385         case SPDY_SETTINGS_FRAME:
386             return State.READ_SETTINGS_FRAME;
387 
388         case SPDY_PING_FRAME:
389             return State.READ_PING_FRAME;
390 
391         case SPDY_GOAWAY_FRAME:
392             return State.READ_GOAWAY_FRAME;
393 
394         case SPDY_HEADERS_FRAME:
395             return State.READ_HEADERS_FRAME;
396 
397         case SPDY_WINDOW_UPDATE_FRAME:
398             return State.READ_WINDOW_UPDATE_FRAME;
399 
400         default:
401             if (length != 0) {
402                 return State.DISCARD_FRAME;
403             } else {
404                 return State.READ_COMMON_HEADER;
405             }
406         }
407     }
408 
409     private static boolean isValidFrameHeader(int streamId, int type, byte flags, int length) {
410         switch (type) {
411         case SPDY_DATA_FRAME:
412             return streamId != 0;
413 
414         case SPDY_SYN_STREAM_FRAME:
415             return length >= 10;
416 
417         case SPDY_SYN_REPLY_FRAME:
418             return length >= 4;
419 
420         case SPDY_RST_STREAM_FRAME:
421             return flags == 0 && length == 8;
422 
423         case SPDY_SETTINGS_FRAME:
424             return length >= 4;
425 
426         case SPDY_PING_FRAME:
427             return length == 4;
428 
429         case SPDY_GOAWAY_FRAME:
430             return length == 8;
431 
432         case SPDY_HEADERS_FRAME:
433             return length >= 4;
434 
435         case SPDY_WINDOW_UPDATE_FRAME:
436             return length == 8;
437 
438         default:
439             return true;
440         }
441     }
442 }