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