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