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