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.ByteBufAllocator;
20  import io.netty.util.internal.ObjectUtil;
21  
22  import static io.netty.handler.codec.spdy.SpdyCodecUtil.getSignedInt;
23  
24  public class SpdyHeaderBlockRawDecoder extends SpdyHeaderBlockDecoder {
25  
26      private static final int LENGTH_FIELD_SIZE = 4;
27  
28      private final int maxHeaderSize;
29  
30      private State state;
31  
32      private ByteBuf cumulation;
33  
34      private int headerSize;
35      private int numHeaders;
36      private int length;
37      private String name;
38  
39      private enum State {
40          READ_NUM_HEADERS,
41          READ_NAME_LENGTH,
42          READ_NAME,
43          SKIP_NAME,
44          READ_VALUE_LENGTH,
45          READ_VALUE,
46          SKIP_VALUE,
47          END_HEADER_BLOCK,
48          ERROR
49      }
50  
51      public SpdyHeaderBlockRawDecoder(SpdyVersion spdyVersion, int maxHeaderSize) {
52          ObjectUtil.checkNotNull(spdyVersion, "spdyVersion");
53          this.maxHeaderSize = maxHeaderSize;
54          state = State.READ_NUM_HEADERS;
55      }
56  
57      private static int readLengthField(ByteBuf buffer) {
58          int length = getSignedInt(buffer, buffer.readerIndex());
59          buffer.skipBytes(LENGTH_FIELD_SIZE);
60          return length;
61      }
62  
63      @Override
64      void decode(ByteBufAllocator alloc, ByteBuf headerBlock, SpdyHeadersFrame frame) throws Exception {
65          ObjectUtil.checkNotNull(headerBlock, "headerBlock");
66          ObjectUtil.checkNotNull(frame, "frame");
67  
68          if (cumulation == null) {
69              decodeHeaderBlock(headerBlock, frame);
70              if (headerBlock.isReadable()) {
71                  cumulation = alloc.buffer(headerBlock.readableBytes());
72                  cumulation.writeBytes(headerBlock);
73              }
74          } else {
75              cumulation.writeBytes(headerBlock);
76              decodeHeaderBlock(cumulation, frame);
77              if (cumulation.isReadable()) {
78                  cumulation.discardReadBytes();
79              } else {
80                  releaseBuffer();
81              }
82          }
83      }
84  
85      protected void decodeHeaderBlock(ByteBuf headerBlock, SpdyHeadersFrame frame) throws Exception {
86          int skipLength;
87          while (headerBlock.isReadable()) {
88              switch(state) {
89                  case READ_NUM_HEADERS:
90                      if (headerBlock.readableBytes() < LENGTH_FIELD_SIZE) {
91                          return;
92                      }
93  
94                      numHeaders = readLengthField(headerBlock);
95  
96                      if (numHeaders < 0) {
97                          state = State.ERROR;
98                          frame.setInvalid();
99                      } else if (numHeaders == 0) {
100                         state = State.END_HEADER_BLOCK;
101                     } else {
102                         state = State.READ_NAME_LENGTH;
103                     }
104                     break;
105 
106                 case READ_NAME_LENGTH:
107                     if (headerBlock.readableBytes() < LENGTH_FIELD_SIZE) {
108                         return;
109                     }
110 
111                     length = readLengthField(headerBlock);
112 
113                     // Recipients of a zero-length name must issue a stream error
114                     if (length <= 0) {
115                         state = State.ERROR;
116                         frame.setInvalid();
117                     } else if (length > maxHeaderSize || headerSize > maxHeaderSize - length) {
118                         headerSize = maxHeaderSize + 1;
119                         state = State.SKIP_NAME;
120                         frame.setTruncated();
121                     } else {
122                         headerSize += length;
123                         state = State.READ_NAME;
124                     }
125                     break;
126 
127                 case READ_NAME:
128                     if (headerBlock.readableBytes() < length) {
129                         return;
130                     }
131 
132                     byte[] nameBytes = new byte[length];
133                     headerBlock.readBytes(nameBytes);
134                     name = new String(nameBytes, "UTF-8");
135 
136                     // Check for identically named headers
137                     if (frame.headers().contains(name)) {
138                         state = State.ERROR;
139                         frame.setInvalid();
140                     } else {
141                         state = State.READ_VALUE_LENGTH;
142                     }
143                     break;
144 
145                 case SKIP_NAME:
146                     skipLength = Math.min(headerBlock.readableBytes(), length);
147                     headerBlock.skipBytes(skipLength);
148                     length -= skipLength;
149 
150                     if (length == 0) {
151                         state = State.READ_VALUE_LENGTH;
152                     }
153                     break;
154 
155                 case READ_VALUE_LENGTH:
156                     if (headerBlock.readableBytes() < LENGTH_FIELD_SIZE) {
157                         return;
158                     }
159 
160                     length = readLengthField(headerBlock);
161 
162                     // Recipients of illegal value fields must issue a stream error
163                     if (length < 0) {
164                         state = State.ERROR;
165                         frame.setInvalid();
166                     } else if (length == 0) {
167                         if (!frame.isTruncated()) {
168                             // SPDY/3 allows zero-length (empty) header values
169                             frame.headers().add(name, "");
170                         }
171 
172                         name = null;
173                         if (--numHeaders == 0) {
174                             state = State.END_HEADER_BLOCK;
175                         } else {
176                             state = State.READ_NAME_LENGTH;
177                         }
178 
179                     } else if (length > maxHeaderSize || headerSize > maxHeaderSize - length) {
180                         headerSize = maxHeaderSize + 1;
181                         name = null;
182                         state = State.SKIP_VALUE;
183                         frame.setTruncated();
184                     } else {
185                         headerSize += length;
186                         state = State.READ_VALUE;
187                     }
188                     break;
189 
190                 case READ_VALUE:
191                     if (headerBlock.readableBytes() < length) {
192                         return;
193                     }
194 
195                     byte[] valueBytes = new byte[length];
196                     headerBlock.readBytes(valueBytes);
197 
198                     // Add Name/Value pair to headers
199                     int index = 0;
200                     int offset = 0;
201 
202                     // Value must not start with a NULL character
203                     if (valueBytes[0] == (byte) 0) {
204                         state = State.ERROR;
205                         frame.setInvalid();
206                         break;
207                     }
208 
209                     while (index < length) {
210                         while (index < valueBytes.length && valueBytes[index] != (byte) 0) {
211                             index ++;
212                         }
213                         if (index < valueBytes.length) {
214                             // Received NULL character
215                             if (index + 1 == valueBytes.length || valueBytes[index + 1] == (byte) 0) {
216                                 // Value field ended with a NULL character or
217                                 // received multiple, in-sequence NULL characters.
218                                 // Recipients of illegal value fields must issue a stream error
219                                 state = State.ERROR;
220                                 frame.setInvalid();
221                                 break;
222                             }
223                         }
224                         String value = new String(valueBytes, offset, index - offset, "UTF-8");
225 
226                         try {
227                             frame.headers().add(name, value);
228                         } catch (IllegalArgumentException e) {
229                             // Name contains NULL or non-ascii characters
230                             state = State.ERROR;
231                             frame.setInvalid();
232                             break;
233                         }
234                         index ++;
235                         offset = index;
236                     }
237 
238                     name = null;
239 
240                     // If we broke out of the add header loop, break here
241                     if (state == State.ERROR) {
242                         break;
243                     }
244 
245                     if (--numHeaders == 0) {
246                         state = State.END_HEADER_BLOCK;
247                     } else {
248                         state = State.READ_NAME_LENGTH;
249                     }
250                     break;
251 
252                 case SKIP_VALUE:
253                     skipLength = Math.min(headerBlock.readableBytes(), length);
254                     headerBlock.skipBytes(skipLength);
255                     length -= skipLength;
256 
257                     if (length == 0) {
258                         if (--numHeaders == 0) {
259                             state = State.END_HEADER_BLOCK;
260                         } else {
261                             state = State.READ_NAME_LENGTH;
262                         }
263                     }
264                     break;
265 
266                 case END_HEADER_BLOCK:
267                     state = State.ERROR;
268                     frame.setInvalid();
269                     break;
270 
271                 case ERROR:
272                     headerBlock.skipBytes(headerBlock.readableBytes());
273                     return;
274 
275                 default:
276                     throw new Error("Shouldn't reach here.");
277             }
278         }
279     }
280 
281     @Override
282     void endHeaderBlock(SpdyHeadersFrame frame) throws Exception {
283         if (state != State.END_HEADER_BLOCK) {
284             frame.setInvalid();
285         }
286 
287         releaseBuffer();
288 
289         // Initialize header block decoding fields
290         headerSize = 0;
291         name = null;
292         state = State.READ_NUM_HEADERS;
293     }
294 
295     @Override
296     void end() {
297         releaseBuffer();
298     }
299 
300     private void releaseBuffer() {
301         if (cumulation != null) {
302             cumulation.release();
303             cumulation = null;
304         }
305     }
306 }