View Javadoc

1   /*
2    * Copyright 2012 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  import org.jboss.netty.channel.ChannelDownstreamHandler;
21  import org.jboss.netty.channel.ChannelEvent;
22  import org.jboss.netty.channel.ChannelHandlerContext;
23  import org.jboss.netty.channel.ChannelStateEvent;
24  import org.jboss.netty.channel.Channels;
25  import org.jboss.netty.channel.MessageEvent;
26  
27  import java.nio.ByteOrder;
28  import java.util.Set;
29  
30  import static org.jboss.netty.handler.codec.spdy.SpdyCodecUtil.*;
31  
32  /**
33   * Encodes a SPDY Data or Control Frame into a {@link ChannelBuffer}.
34   */
35  public class SpdyFrameEncoder implements ChannelDownstreamHandler {
36  
37      private final int version;
38      private volatile boolean finished;
39      private final SpdyHeaderBlockCompressor headerBlockCompressor;
40  
41      /**
42       * Creates a new instance with the default {@code version (2)},
43       * {@code compressionLevel (6)}, {@code windowBits (15)},
44       * and {@code memLevel (8)}.
45       */
46      @Deprecated
47      public SpdyFrameEncoder() {
48          this(2, 6, 15, 8);
49      }
50  
51      /**
52       * Creates a new instance with the specified {@code version} and the
53       * default {@code compressionLevel (6)}, {@code windowBits (15)},
54       * and {@code memLevel (8)}.
55       */
56      public SpdyFrameEncoder(int version) {
57          this(version, 6, 15, 8);
58      }
59  
60      /**
61       * Creates a new instance with the default {@code version (2)} and the
62       * specified parameters.
63       */
64      @Deprecated
65      public SpdyFrameEncoder(int compressionLevel, int windowBits, int memLevel) {
66          this(2, compressionLevel, windowBits, memLevel);
67      }
68  
69      /**
70       * Creates a new instance with the specified parameters.
71       */
72      public SpdyFrameEncoder(int version, int compressionLevel, int windowBits, int memLevel) {
73          if (version < SPDY_MIN_VERSION || version > SPDY_MAX_VERSION) {
74              throw new IllegalArgumentException(
75                      "unknown version: " + version);
76          }
77          this.version = version;
78          headerBlockCompressor = SpdyHeaderBlockCompressor.newInstance(
79                  version, compressionLevel, windowBits, memLevel);
80      }
81  
82      public void handleDownstream(
83              final ChannelHandlerContext ctx, ChannelEvent evt) throws Exception {
84          if (evt instanceof ChannelStateEvent) {
85              ChannelStateEvent e = (ChannelStateEvent) evt;
86              switch (e.getState()) {
87              case OPEN:
88              case CONNECTED:
89              case BOUND:
90                  if (Boolean.FALSE.equals(e.getValue()) || e.getValue() == null) {
91                      synchronized (headerBlockCompressor) {
92                          finished = true;
93                          headerBlockCompressor.end();
94                      }
95                  }
96              }
97          }
98  
99          if (!(evt instanceof MessageEvent)) {
100             ctx.sendDownstream(evt);
101             return;
102         }
103 
104         final MessageEvent e = (MessageEvent) evt;
105         Object msg = e.getMessage();
106 
107         if (msg instanceof SpdyDataFrame) {
108 
109             SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
110             ChannelBuffer data = spdyDataFrame.getData();
111             byte flags = spdyDataFrame.isLast() ? SPDY_DATA_FLAG_FIN : 0;
112             ChannelBuffer header = ChannelBuffers.buffer(
113                     ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE);
114             header.writeInt(spdyDataFrame.getStreamId() & 0x7FFFFFFF);
115             header.writeByte(flags);
116             header.writeMedium(data.readableBytes());
117             ChannelBuffer frame = ChannelBuffers.wrappedBuffer(header, data);
118             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
119             return;
120         }
121 
122         if (msg instanceof SpdySynStreamFrame) {
123 
124             synchronized (headerBlockCompressor) {
125                 SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
126                 ChannelBuffer data = compressHeaderBlock(
127                         encodeHeaderBlock(version, spdySynStreamFrame));
128                 byte flags = spdySynStreamFrame.isLast() ? SPDY_FLAG_FIN : 0;
129                 if (spdySynStreamFrame.isUnidirectional()) {
130                     flags |= SPDY_FLAG_UNIDIRECTIONAL;
131                 }
132                 int headerBlockLength = data.readableBytes();
133                 int length;
134                 if (version < 3) {
135                     length = headerBlockLength == 0 ? 12 : 10 + headerBlockLength;
136                 } else {
137                     length = 10 + headerBlockLength;
138                 }
139                 ChannelBuffer frame = ChannelBuffers.buffer(
140                         ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 12);
141                 frame.writeShort(version | 0x8000);
142                 frame.writeShort(SPDY_SYN_STREAM_FRAME);
143                 frame.writeByte(flags);
144                 frame.writeMedium(length);
145                 frame.writeInt(spdySynStreamFrame.getStreamId());
146                 frame.writeInt(spdySynStreamFrame.getAssociatedToStreamId());
147                 if (version < 3) {
148                     // Restrict priorities for SPDY/2 to between 0 and 3
149                     byte priority = spdySynStreamFrame.getPriority();
150                     if (priority > 3) {
151                         priority = 3;
152                     }
153                     frame.writeShort((priority & 0xFF) << 14);
154                 } else {
155                     frame.writeShort((spdySynStreamFrame.getPriority() & 0xFF) << 13);
156                 }
157                 if (version < 3 && data.readableBytes() == 0) {
158                     frame.writeShort(0);
159                 }
160                 // Writes of compressed data must occur in order
161                 final ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(frame, data);
162                 Channels.write(ctx, e.getFuture(), buffer, e.getRemoteAddress());
163             }
164             return;
165         }
166 
167         if (msg instanceof SpdySynReplyFrame) {
168 
169             synchronized (headerBlockCompressor) {
170                 SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
171                 ChannelBuffer data = compressHeaderBlock(
172                         encodeHeaderBlock(version, spdySynReplyFrame));
173                 byte flags = spdySynReplyFrame.isLast() ? SPDY_FLAG_FIN : 0;
174                 int headerBlockLength = data.readableBytes();
175                 int length;
176                 if (version < 3) {
177                     length = headerBlockLength == 0 ? 8 : 6 + headerBlockLength;
178                 } else {
179                     length = 4 + headerBlockLength;
180                 }
181                 ChannelBuffer frame = ChannelBuffers.buffer(
182                         ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 8);
183                 frame.writeShort(version | 0x8000);
184                 frame.writeShort(SPDY_SYN_REPLY_FRAME);
185                 frame.writeByte(flags);
186                 frame.writeMedium(length);
187                 frame.writeInt(spdySynReplyFrame.getStreamId());
188                 if (version < 3) {
189                     if (data.readableBytes() == 0) {
190                         frame.writeInt(0);
191                     } else {
192                         frame.writeShort(0);
193                     }
194                 }
195                 // Writes of compressed data must occur in order
196                 final ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(frame, data);
197                 Channels.write(ctx, e.getFuture(), buffer, e.getRemoteAddress());
198             }
199             return;
200         }
201 
202         if (msg instanceof SpdyRstStreamFrame) {
203 
204             SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
205             ChannelBuffer frame = ChannelBuffers.buffer(
206                     ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 8);
207             frame.writeShort(version | 0x8000);
208             frame.writeShort(SPDY_RST_STREAM_FRAME);
209             frame.writeInt(8);
210             frame.writeInt(spdyRstStreamFrame.getStreamId());
211             frame.writeInt(spdyRstStreamFrame.getStatus().getCode());
212             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
213             return;
214         }
215 
216         if (msg instanceof SpdySettingsFrame) {
217 
218             SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg;
219             byte flags = spdySettingsFrame.clearPreviouslyPersistedSettings() ?
220                 SPDY_SETTINGS_CLEAR : 0;
221             Set<Integer> IDs = spdySettingsFrame.getIds();
222             int numEntries = IDs.size();
223             int length = 4 + numEntries * 8;
224             ChannelBuffer frame = ChannelBuffers.buffer(
225                     ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + length);
226             frame.writeShort(version | 0x8000);
227             frame.writeShort(SPDY_SETTINGS_FRAME);
228             frame.writeByte(flags);
229             frame.writeMedium(length);
230             frame.writeInt(numEntries);
231             for (Integer ID: IDs) {
232                 int id = ID.intValue();
233                 byte ID_flags = 0;
234                 if (spdySettingsFrame.isPersistValue(id)) {
235                     ID_flags |= SPDY_SETTINGS_PERSIST_VALUE;
236                 }
237                 if (spdySettingsFrame.isPersisted(id)) {
238                     ID_flags |= SPDY_SETTINGS_PERSISTED;
239                 }
240                 if (version < 3) {
241                     // Chromium Issue 79156
242                     // SPDY setting ids are not written in network byte order
243                     // Write id assuming the architecture is little endian
244                     frame.writeByte(id & 0xFF);
245                     frame.writeByte(id >>  8 & 0xFF);
246                     frame.writeByte(id >> 16 & 0xFF);
247                     frame.writeByte(ID_flags);
248                 } else {
249                     frame.writeByte(ID_flags);
250                     frame.writeMedium(id);
251                 }
252                 frame.writeInt(spdySettingsFrame.getValue(id));
253             }
254             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
255             return;
256         }
257 
258         if (msg instanceof SpdyNoOpFrame) {
259 
260             ChannelBuffer frame = ChannelBuffers.buffer(
261                     ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE);
262             frame.writeShort(version | 0x8000);
263             frame.writeShort(SPDY_NOOP_FRAME);
264             frame.writeInt(0);
265             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
266             return;
267         }
268 
269         if (msg instanceof SpdyPingFrame) {
270 
271             SpdyPingFrame spdyPingFrame = (SpdyPingFrame) msg;
272             ChannelBuffer frame = ChannelBuffers.buffer(
273                     ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 4);
274             frame.writeShort(version | 0x8000);
275             frame.writeShort(SPDY_PING_FRAME);
276             frame.writeInt(4);
277             frame.writeInt(spdyPingFrame.getId());
278             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
279             return;
280         }
281 
282         if (msg instanceof SpdyGoAwayFrame) {
283 
284             SpdyGoAwayFrame spdyGoAwayFrame = (SpdyGoAwayFrame) msg;
285             int length = version < 3 ? 4 : 8;
286             ChannelBuffer frame = ChannelBuffers.buffer(
287                     ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + length);
288             frame.writeShort(version | 0x8000);
289             frame.writeShort(SPDY_GOAWAY_FRAME);
290             frame.writeInt(length);
291             frame.writeInt(spdyGoAwayFrame.getLastGoodStreamId());
292             if (version >= 3) {
293                 frame.writeInt(spdyGoAwayFrame.getStatus().getCode());
294             }
295             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
296             return;
297         }
298 
299         if (msg instanceof SpdyHeadersFrame) {
300 
301             synchronized (headerBlockCompressor) {
302                 SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
303                 ChannelBuffer data = compressHeaderBlock(
304                         encodeHeaderBlock(version, spdyHeadersFrame));
305                 byte flags = spdyHeadersFrame.isLast() ? SPDY_FLAG_FIN : 0;
306                 int headerBlockLength = data.readableBytes();
307                 int length;
308                 if (version < 3) {
309                     length = headerBlockLength == 0 ? 4 : 6 + headerBlockLength;
310                 } else {
311                     length = 4 + headerBlockLength;
312                 }
313                 ChannelBuffer frame = ChannelBuffers.buffer(
314                         ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + length);
315                 frame.writeShort(version | 0x8000);
316                 frame.writeShort(SPDY_HEADERS_FRAME);
317                 frame.writeByte(flags);
318                 frame.writeMedium(length);
319                 frame.writeInt(spdyHeadersFrame.getStreamId());
320                 if (version < 3 && data.readableBytes() != 0) {
321                     frame.writeShort(0);
322                 }
323                 // Writes of compressed data must occur in order
324                 final ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(frame, data);
325                 Channels.write(ctx, e.getFuture(), buffer, e.getRemoteAddress());
326             }
327             return;
328         }
329 
330         if (msg instanceof SpdyWindowUpdateFrame) {
331             SpdyWindowUpdateFrame spdyWindowUpdateFrame = (SpdyWindowUpdateFrame) msg;
332             ChannelBuffer frame = ChannelBuffers.buffer(
333                     ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 8);
334             frame.writeShort(version | 0x8000);
335             frame.writeShort(SPDY_WINDOW_UPDATE_FRAME);
336             frame.writeInt(8);
337             frame.writeInt(spdyWindowUpdateFrame.getStreamId());
338             frame.writeInt(spdyWindowUpdateFrame.getDeltaWindowSize());
339             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
340             return;
341         }
342 
343         // Unknown message type
344         ctx.sendDownstream(evt);
345     }
346 
347     private static void writeLengthField(int version, ChannelBuffer buffer, int length) {
348         if (version < 3) {
349             buffer.writeShort(length);
350         } else {
351             buffer.writeInt(length);
352         }
353     }
354 
355     private static void setLengthField(int version, ChannelBuffer buffer, int writerIndex, int length) {
356         if (version < 3) {
357             buffer.setShort(writerIndex, length);
358         } else {
359             buffer.setInt(writerIndex, length);
360         }
361     }
362 
363     private static ChannelBuffer encodeHeaderBlock(int version, SpdyHeaderBlock headerFrame)
364             throws Exception {
365         Set<String> names = headerFrame.getHeaderNames();
366         int numHeaders = names.size();
367         if (numHeaders == 0) {
368             return ChannelBuffers.EMPTY_BUFFER;
369         }
370         if (numHeaders > SPDY_MAX_NV_LENGTH) {
371             throw new IllegalArgumentException(
372                     "header block contains too many headers");
373         }
374         ChannelBuffer headerBlock = ChannelBuffers.dynamicBuffer(
375                 ByteOrder.BIG_ENDIAN, 256);
376         writeLengthField(version, headerBlock, numHeaders);
377         for (String name: names) {
378             byte[] nameBytes = name.getBytes("UTF-8");
379             writeLengthField(version, headerBlock, nameBytes.length);
380             headerBlock.writeBytes(nameBytes);
381             int savedIndex = headerBlock.writerIndex();
382             int valueLength = 0;
383             writeLengthField(version, headerBlock, valueLength);
384             for (String value: headerFrame.getHeaders(name)) {
385                 byte[] valueBytes = value.getBytes("UTF-8");
386                 if (valueBytes.length > 0) {
387                     headerBlock.writeBytes(valueBytes);
388                     headerBlock.writeByte(0);
389                     valueLength += valueBytes.length + 1;
390                 }
391             }
392             if (valueLength == 0) {
393                 if (version < 3) {
394                     throw new IllegalArgumentException(
395                             "header value cannot be empty: " + name);
396                 }
397             } else {
398                 valueLength --;
399             }
400             if (valueLength > SPDY_MAX_NV_LENGTH) {
401                 throw new IllegalArgumentException(
402                         "header exceeds allowable length: " + name);
403             }
404             if (valueLength > 0) {
405                 setLengthField(version, headerBlock, savedIndex, valueLength);
406                 headerBlock.writerIndex(headerBlock.writerIndex() - 1);
407             }
408         }
409         return headerBlock;
410     }
411 
412     // always called while synchronized on headerBlockCompressor
413     private ChannelBuffer compressHeaderBlock(ChannelBuffer uncompressed)
414             throws Exception {
415         if (uncompressed.readableBytes() == 0) {
416             return ChannelBuffers.EMPTY_BUFFER;
417         }
418         ChannelBuffer compressed = ChannelBuffers.dynamicBuffer();
419         if (!finished) {
420             headerBlockCompressor.setInput(uncompressed);
421             headerBlockCompressor.encode(compressed);
422         }
423         return compressed;
424     }
425 }