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 static org.jboss.netty.handler.codec.spdy.SpdyCodecUtil.*;
19  
20  import java.nio.ByteOrder;
21  import java.util.Set;
22  
23  import org.jboss.netty.buffer.ChannelBuffer;
24  import org.jboss.netty.buffer.ChannelBuffers;
25  import org.jboss.netty.channel.ChannelDownstreamHandler;
26  import org.jboss.netty.channel.ChannelEvent;
27  import org.jboss.netty.channel.ChannelHandlerContext;
28  import org.jboss.netty.channel.ChannelStateEvent;
29  import org.jboss.netty.channel.Channels;
30  import org.jboss.netty.channel.MessageEvent;
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         } else if (msg instanceof SpdySynStreamFrame) {
122 
123             synchronized (headerBlockCompressor) {
124                 SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
125                 ChannelBuffer data = compressHeaderBlock(
126                         encodeHeaderBlock(version, spdySynStreamFrame));
127                 byte flags = spdySynStreamFrame.isLast() ? SPDY_FLAG_FIN : 0;
128                 if (spdySynStreamFrame.isUnidirectional()) {
129                     flags |= SPDY_FLAG_UNIDIRECTIONAL;
130                 }
131                 int headerBlockLength = data.readableBytes();
132                 int length;
133                 if (version < 3) {
134                     length = headerBlockLength == 0 ? 12 : 10 + headerBlockLength;
135                 } else {
136                     length = 10 + headerBlockLength;
137                 }
138                 ChannelBuffer frame = ChannelBuffers.buffer(
139                         ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 12);
140                 frame.writeShort(version | 0x8000);
141                 frame.writeShort(SPDY_SYN_STREAM_FRAME);
142                 frame.writeByte(flags);
143                 frame.writeMedium(length);
144                 frame.writeInt(spdySynStreamFrame.getStreamId());
145                 frame.writeInt(spdySynStreamFrame.getAssociatedToStreamId());
146                 if (version < 3) {
147                     // Restrict priorities for SPDY/2 to between 0 and 3
148                     byte priority = spdySynStreamFrame.getPriority();
149                     if (priority > 3) {
150                         priority = 3;
151                     }
152                     frame.writeShort((priority & 0xFF) << 14);
153                 } else {
154                     frame.writeShort((spdySynStreamFrame.getPriority() & 0xFF) << 13);
155                 }
156                 if (version < 3 && data.readableBytes() == 0) {
157                     frame.writeShort(0);
158                 }
159                 // Writes of compressed data must occur in order
160                 final ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(frame, data);
161                 Channels.write(ctx, e.getFuture(), buffer, e.getRemoteAddress());
162             }
163             return;
164 
165         } else if (msg instanceof SpdySynReplyFrame) {
166 
167             synchronized (headerBlockCompressor) {
168                 SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
169                 ChannelBuffer data = compressHeaderBlock(
170                         encodeHeaderBlock(version, spdySynReplyFrame));
171                 byte flags = spdySynReplyFrame.isLast() ? SPDY_FLAG_FIN : 0;
172                 int headerBlockLength = data.readableBytes();
173                 int length;
174                 if (version < 3) {
175                     length = headerBlockLength == 0 ? 8 : 6 + headerBlockLength;
176                 } else {
177                     length = 4 + headerBlockLength;
178                 }
179                 ChannelBuffer frame = ChannelBuffers.buffer(
180                         ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 8);
181                 frame.writeShort(version | 0x8000);
182                 frame.writeShort(SPDY_SYN_REPLY_FRAME);
183                 frame.writeByte(flags);
184                 frame.writeMedium(length);
185                 frame.writeInt(spdySynReplyFrame.getStreamId());
186                 if (version < 3) {
187                     if (data.readableBytes() == 0) {
188                         frame.writeInt(0);
189                     } else {
190                         frame.writeShort(0);
191                     }
192                 }
193                 // Writes of compressed data must occur in order
194                 final ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(frame, data);
195                 Channels.write(ctx, e.getFuture(), buffer, e.getRemoteAddress());
196             }
197             return;
198 
199         } else if (msg instanceof SpdyRstStreamFrame) {
200 
201             SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
202             ChannelBuffer frame = ChannelBuffers.buffer(
203                     ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 8);
204             frame.writeShort(version | 0x8000);
205             frame.writeShort(SPDY_RST_STREAM_FRAME);
206             frame.writeInt(8);
207             frame.writeInt(spdyRstStreamFrame.getStreamId());
208             frame.writeInt(spdyRstStreamFrame.getStatus().getCode());
209             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
210             return;
211 
212         } else if (msg instanceof SpdySettingsFrame) {
213 
214             SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg;
215             byte flags = spdySettingsFrame.clearPreviouslyPersistedSettings() ?
216                 SPDY_SETTINGS_CLEAR : 0;
217             Set<Integer> IDs = spdySettingsFrame.getIds();
218             int numEntries = IDs.size();
219             int length = 4 + numEntries * 8;
220             ChannelBuffer frame = ChannelBuffers.buffer(
221                     ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + length);
222             frame.writeShort(version | 0x8000);
223             frame.writeShort(SPDY_SETTINGS_FRAME);
224             frame.writeByte(flags);
225             frame.writeMedium(length);
226             frame.writeInt(numEntries);
227             for (Integer ID: IDs) {
228                 int id = ID.intValue();
229                 byte ID_flags = (byte) 0;
230                 if (spdySettingsFrame.isPersistValue(id)) {
231                     ID_flags |= SPDY_SETTINGS_PERSIST_VALUE;
232                 }
233                 if (spdySettingsFrame.isPersisted(id)) {
234                     ID_flags |= SPDY_SETTINGS_PERSISTED;
235                 }
236                 if (version < 3) {
237                     // Chromium Issue 79156
238                     // SPDY setting ids are not written in network byte order
239                     // Write id assuming the architecture is little endian
240                     frame.writeByte(id & 0xFF);
241                     frame.writeByte(id >>  8 & 0xFF);
242                     frame.writeByte(id >> 16 & 0xFF);
243                     frame.writeByte(ID_flags);
244                 } else {
245                     frame.writeByte(ID_flags);
246                     frame.writeMedium(id);
247                 }
248                 frame.writeInt(spdySettingsFrame.getValue(id));
249             }
250             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
251             return;
252 
253         } else if (msg instanceof SpdyNoOpFrame) {
254 
255             ChannelBuffer frame = ChannelBuffers.buffer(
256                     ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE);
257             frame.writeShort(version | 0x8000);
258             frame.writeShort(SPDY_NOOP_FRAME);
259             frame.writeInt(0);
260             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
261             return;
262 
263         } else if (msg instanceof SpdyPingFrame) {
264 
265             SpdyPingFrame spdyPingFrame = (SpdyPingFrame) msg;
266             ChannelBuffer frame = ChannelBuffers.buffer(
267                     ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 4);
268             frame.writeShort(version | 0x8000);
269             frame.writeShort(SPDY_PING_FRAME);
270             frame.writeInt(4);
271             frame.writeInt(spdyPingFrame.getId());
272             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
273             return;
274 
275         } else if (msg instanceof SpdyGoAwayFrame) {
276 
277             SpdyGoAwayFrame spdyGoAwayFrame = (SpdyGoAwayFrame) msg;
278             int length = version < 3 ? 4 : 8;
279             ChannelBuffer frame = ChannelBuffers.buffer(
280                     ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + length);
281             frame.writeShort(version | 0x8000);
282             frame.writeShort(SPDY_GOAWAY_FRAME);
283             frame.writeInt(length);
284             frame.writeInt(spdyGoAwayFrame.getLastGoodStreamId());
285             if (version >= 3) {
286                 frame.writeInt(spdyGoAwayFrame.getStatus().getCode());
287             }
288             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
289             return;
290 
291         } else if (msg instanceof SpdyHeadersFrame) {
292 
293             synchronized (headerBlockCompressor) {
294                 SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
295                 ChannelBuffer data = compressHeaderBlock(
296                         encodeHeaderBlock(version, spdyHeadersFrame));
297                 byte flags = spdyHeadersFrame.isLast() ? SPDY_FLAG_FIN : 0;
298                 int headerBlockLength = data.readableBytes();
299                 int length;
300                 if (version < 3) {
301                     length = headerBlockLength == 0 ? 4 : 6 + headerBlockLength;
302                 } else {
303                     length = 4 + headerBlockLength;
304                 }
305                 ChannelBuffer frame = ChannelBuffers.buffer(
306                         ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + length);
307                 frame.writeShort(version | 0x8000);
308                 frame.writeShort(SPDY_HEADERS_FRAME);
309                 frame.writeByte(flags);
310                 frame.writeMedium(length);
311                 frame.writeInt(spdyHeadersFrame.getStreamId());
312                 if (version < 3 && data.readableBytes() != 0) {
313                     frame.writeShort(0);
314                 }
315                 // Writes of compressed data must occur in order
316                 final ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(frame, data);
317                 Channels.write(ctx, e.getFuture(), buffer, e.getRemoteAddress());
318             }
319             return;
320 
321         } else if (msg instanceof SpdyWindowUpdateFrame) {
322 
323             SpdyWindowUpdateFrame spdyWindowUpdateFrame = (SpdyWindowUpdateFrame) msg;
324             ChannelBuffer frame = ChannelBuffers.buffer(
325                     ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 8);
326             frame.writeShort(version | 0x8000);
327             frame.writeShort(SPDY_WINDOW_UPDATE_FRAME);
328             frame.writeInt(8);
329             frame.writeInt(spdyWindowUpdateFrame.getStreamId());
330             frame.writeInt(spdyWindowUpdateFrame.getDeltaWindowSize());
331             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
332             return;
333         }
334 
335         // Unknown message type
336         ctx.sendDownstream(evt);
337     }
338 
339     private static void writeLengthField(int version, ChannelBuffer buffer, int length) {
340         if (version < 3) {
341             buffer.writeShort(length);
342         } else {
343             buffer.writeInt(length);
344         }
345     }
346 
347     private static void setLengthField(int version, ChannelBuffer buffer, int writerIndex, int length) {
348         if (version < 3) {
349             buffer.setShort(writerIndex, length);
350         } else {
351             buffer.setInt(writerIndex, length);
352         }
353     }
354 
355     private static ChannelBuffer encodeHeaderBlock(int version, SpdyHeaderBlock headerFrame)
356             throws Exception {
357         Set<String> names = headerFrame.getHeaderNames();
358         int numHeaders = names.size();
359         if (numHeaders == 0) {
360             return ChannelBuffers.EMPTY_BUFFER;
361         }
362         if (numHeaders > SPDY_MAX_NV_LENGTH) {
363             throw new IllegalArgumentException(
364                     "header block contains too many headers");
365         }
366         ChannelBuffer headerBlock = ChannelBuffers.dynamicBuffer(
367                 ByteOrder.BIG_ENDIAN, 256);
368         writeLengthField(version, headerBlock, numHeaders);
369         for (String name: names) {
370             byte[] nameBytes = name.getBytes("UTF-8");
371             writeLengthField(version, headerBlock, nameBytes.length);
372             headerBlock.writeBytes(nameBytes);
373             int savedIndex = headerBlock.writerIndex();
374             int valueLength = 0;
375             writeLengthField(version, headerBlock, valueLength);
376             for (String value: headerFrame.getHeaders(name)) {
377                 byte[] valueBytes = value.getBytes("UTF-8");
378                 if (valueBytes.length > 0) {
379                     headerBlock.writeBytes(valueBytes);
380                     headerBlock.writeByte(0);
381                     valueLength += valueBytes.length + 1;
382                 }
383             }
384             if (valueLength == 0) {
385                 if (version < 3) {
386                     throw new IllegalArgumentException(
387                             "header value cannot be empty: " + name);
388                 }
389             } else {
390                 valueLength --;
391             }
392             if (valueLength > SPDY_MAX_NV_LENGTH) {
393                 throw new IllegalArgumentException(
394                         "header exceeds allowable length: " + name);
395             }
396             if (valueLength > 0) {
397                 setLengthField(version, headerBlock, savedIndex, valueLength);
398                 headerBlock.writerIndex(headerBlock.writerIndex() - 1);
399             }
400         }
401         return headerBlock;
402     }
403 
404     // always called while synchronized on headerBlockCompressor
405     private ChannelBuffer compressHeaderBlock(ChannelBuffer uncompressed)
406             throws Exception {
407         if (uncompressed.readableBytes() == 0) {
408             return ChannelBuffers.EMPTY_BUFFER;
409         }
410         ChannelBuffer compressed = ChannelBuffers.dynamicBuffer();
411         if (!finished) {
412             headerBlockCompressor.setInput(uncompressed);
413             headerBlockCompressor.encode(compressed);
414         }
415         return compressed;
416     }
417 }