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