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