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