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.channel.Channel;
20  import org.jboss.netty.channel.ChannelDownstreamHandler;
21  import org.jboss.netty.channel.ChannelEvent;
22  import org.jboss.netty.channel.ChannelHandler;
23  import org.jboss.netty.channel.ChannelHandlerContext;
24  import org.jboss.netty.channel.ChannelStateEvent;
25  import org.jboss.netty.channel.Channels;
26  import org.jboss.netty.channel.MessageEvent;
27  import org.jboss.netty.handler.codec.frame.FrameDecoder;
28  
29  /**
30   * A {@link ChannelHandler} that encodes and decodes SPDY Frames.
31   * @apiviz.has org.jboss.netty.handler.codec.spdy.SpdyFrameDecoder
32   * @apiviz.has org.jboss.netty.handler.codec.spdy.SpdyFrameEncoder
33   * @apiviz.has org.jboss.netty.handler.codec.spdy.SpdyHeaderBlockDecoder
34   * @apiviz.has org.jboss.netty.handler.codec.spdy.SpdyHeaderBlockEncoder
35   */
36  public class SpdyFrameCodec extends FrameDecoder
37          implements SpdyFrameDecoderDelegate, ChannelDownstreamHandler {
38  
39      private static final SpdyProtocolException INVALID_FRAME =
40          new SpdyProtocolException("Received invalid frame");
41  
42      private final SpdyFrameDecoder spdyFrameDecoder;
43      private final SpdyFrameEncoder spdyFrameEncoder;
44      private final SpdyHeaderBlockDecoder spdyHeaderBlockDecoder;
45      private final SpdyHeaderBlockEncoder spdyHeaderBlockEncoder;
46  
47      private SpdyHeadersFrame spdyHeadersFrame;
48      private SpdySettingsFrame spdySettingsFrame;
49  
50      private volatile ChannelHandlerContext ctx;
51  
52      /**
53       * Creates a new instance with the specified {@code version} and
54       * the default decoder and encoder options
55       * ({@code maxChunkSize (8192)}, {@code maxHeaderSize (16384)},
56       * {@code compressionLevel (6)}, {@code windowBits (15)},
57       * and {@code memLevel (8)}).
58       */
59      public SpdyFrameCodec(SpdyVersion version) {
60          this(version, 8192, 16384, 6, 15, 8);
61      }
62  
63      /**
64       * Creates a new instance with the specified decoder and encoder options.
65       */
66      public SpdyFrameCodec(
67              SpdyVersion version, int maxChunkSize, int maxHeaderSize,
68              int compressionLevel, int windowBits, int memLevel) {
69          this(version, maxChunkSize,
70              SpdyHeaderBlockDecoder.newInstance(version, maxHeaderSize),
71              SpdyHeaderBlockEncoder.newInstance(version, compressionLevel, windowBits, memLevel));
72      }
73  
74      protected SpdyFrameCodec(SpdyVersion version, int maxChunkSize,
75              SpdyHeaderBlockDecoder spdyHeaderBlockDecoder, SpdyHeaderBlockEncoder spdyHeaderBlockEncoder) {
76          spdyFrameDecoder = new SpdyFrameDecoder(version, this, maxChunkSize);
77          spdyFrameEncoder = new SpdyFrameEncoder(version);
78          this.spdyHeaderBlockDecoder = spdyHeaderBlockDecoder;
79          this.spdyHeaderBlockEncoder = spdyHeaderBlockEncoder;
80      }
81  
82      @Override
83      public void beforeAdd(ChannelHandlerContext ctx) throws Exception {
84          super.beforeAdd(ctx);
85          this.ctx = ctx;
86      }
87  
88      @Override
89      protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
90          spdyFrameDecoder.decode(buffer);
91          return null;
92      }
93  
94      @Override
95      protected void cleanup(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
96          try {
97              super.cleanup(ctx, e);
98          } finally {
99              spdyHeaderBlockDecoder.end();
100             synchronized (spdyHeaderBlockEncoder) {
101                 spdyHeaderBlockEncoder.end();
102             }
103         }
104     }
105 
106     public void handleDownstream(ChannelHandlerContext ctx, ChannelEvent evt) throws Exception {
107         if (evt instanceof ChannelStateEvent) {
108             ChannelStateEvent e = (ChannelStateEvent) evt;
109             switch (e.getState()) {
110             case OPEN:
111             case CONNECTED:
112             case BOUND:
113                 if (Boolean.FALSE.equals(e.getValue()) || e.getValue() == null) {
114                     synchronized (spdyHeaderBlockEncoder) {
115                       spdyHeaderBlockEncoder.end();
116                     }
117                 }
118             }
119         }
120 
121         if (!(evt instanceof MessageEvent)) {
122           ctx.sendDownstream(evt);
123           return;
124         }
125 
126         final MessageEvent e = (MessageEvent) evt;
127         Object msg = e.getMessage();
128 
129         if (msg instanceof SpdyDataFrame) {
130 
131             SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
132             ChannelBuffer frame = spdyFrameEncoder.encodeDataFrame(
133                 spdyDataFrame.getStreamId(),
134                 spdyDataFrame.isLast(),
135                 spdyDataFrame.getData()
136             );
137             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
138             return;
139         }
140 
141         if (msg instanceof SpdySynStreamFrame) {
142 
143             synchronized (spdyHeaderBlockEncoder) {
144                 SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
145                 ChannelBuffer frame = spdyFrameEncoder.encodeSynStreamFrame(
146                     spdySynStreamFrame.getStreamId(),
147                     spdySynStreamFrame.getAssociatedToStreamId(),
148                     spdySynStreamFrame.getPriority(),
149                     spdySynStreamFrame.isLast(),
150                     spdySynStreamFrame.isUnidirectional(),
151                     spdyHeaderBlockEncoder.encode(spdySynStreamFrame)
152                 );
153                 // Writes of compressed data must occur in order
154                 Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
155             }
156             return;
157         }
158 
159         if (msg instanceof SpdySynReplyFrame) {
160 
161             synchronized (spdyHeaderBlockEncoder) {
162                 SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
163                 ChannelBuffer frame = spdyFrameEncoder.encodeSynReplyFrame(
164                     spdySynReplyFrame.getStreamId(),
165                     spdySynReplyFrame.isLast(),
166                     spdyHeaderBlockEncoder.encode(spdySynReplyFrame)
167                 );
168                 // Writes of compressed data must occur in order
169                 Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
170             }
171             return;
172         }
173 
174         if (msg instanceof SpdyRstStreamFrame) {
175 
176             SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
177             ChannelBuffer frame = spdyFrameEncoder.encodeRstStreamFrame(
178                 spdyRstStreamFrame.getStreamId(),
179                 spdyRstStreamFrame.getStatus().getCode()
180             );
181             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
182             return;
183         }
184 
185         if (msg instanceof SpdySettingsFrame) {
186 
187             SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg;
188             ChannelBuffer frame = spdyFrameEncoder.encodeSettingsFrame(
189                 spdySettingsFrame
190             );
191             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
192             return;
193         }
194 
195         if (msg instanceof SpdyPingFrame) {
196 
197             SpdyPingFrame spdyPingFrame = (SpdyPingFrame) msg;
198             ChannelBuffer frame = spdyFrameEncoder.encodePingFrame(
199                 spdyPingFrame.getId()
200             );
201             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
202             return;
203         }
204 
205         if (msg instanceof SpdyGoAwayFrame) {
206 
207             SpdyGoAwayFrame spdyGoAwayFrame = (SpdyGoAwayFrame) msg;
208             ChannelBuffer frame = spdyFrameEncoder.encodeGoAwayFrame(
209                 spdyGoAwayFrame.getLastGoodStreamId(),
210                 spdyGoAwayFrame.getStatus().getCode()
211             );
212             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
213             return;
214         }
215 
216         if (msg instanceof SpdyHeadersFrame) {
217 
218             synchronized (spdyHeaderBlockEncoder) {
219                 SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
220                 ChannelBuffer frame = spdyFrameEncoder.encodeHeadersFrame(
221                     spdyHeadersFrame.getStreamId(),
222                     spdyHeadersFrame.isLast(),
223                     spdyHeaderBlockEncoder.encode(spdyHeadersFrame)
224                 );
225                 // Writes of compressed data must occur in order
226                 Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
227             }
228             return;
229         }
230 
231         if (msg instanceof SpdyWindowUpdateFrame) {
232 
233             SpdyWindowUpdateFrame spdyWindowUpdateFrame = (SpdyWindowUpdateFrame) msg;
234             ChannelBuffer frame = spdyFrameEncoder.encodeWindowUpdateFrame(
235                 spdyWindowUpdateFrame.getStreamId(),
236                 spdyWindowUpdateFrame.getDeltaWindowSize()
237             );
238             Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
239             return;
240         }
241 
242         // Unknown message type
243         ctx.sendDownstream(evt);
244     }
245 
246     public void readDataFrame(int streamId, boolean last, ChannelBuffer data) {
247         SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(streamId);
248         spdyDataFrame.setLast(last);
249         spdyDataFrame.setData(data);
250         Channels.fireMessageReceived(ctx, spdyDataFrame);
251     }
252 
253     public void readSynStreamFrame(
254         int streamId, int associatedToStreamId, byte priority, boolean last, boolean unidirectional) {
255         SpdySynStreamFrame spdySynStreamFrame = new DefaultSpdySynStreamFrame(streamId, associatedToStreamId, priority);
256         spdySynStreamFrame.setLast(last);
257         spdySynStreamFrame.setUnidirectional(unidirectional);
258         spdyHeadersFrame = spdySynStreamFrame;
259     }
260 
261     public void readSynReplyFrame(int streamId, boolean last) {
262         SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId);
263         spdySynReplyFrame.setLast(last);
264         spdyHeadersFrame = spdySynReplyFrame;
265     }
266 
267     public void readRstStreamFrame(int streamId, int statusCode) {
268         SpdyRstStreamFrame spdyRstStreamFrame = new DefaultSpdyRstStreamFrame(streamId, statusCode);
269         Channels.fireMessageReceived(ctx, spdyRstStreamFrame);
270     }
271 
272     public void readSettingsFrame(boolean clearPersisted) {
273         spdySettingsFrame = new DefaultSpdySettingsFrame();
274         spdySettingsFrame.setClearPreviouslyPersistedSettings(clearPersisted);
275     }
276 
277     public void readSetting(int id, int value, boolean persistValue, boolean persisted) {
278         spdySettingsFrame.setValue(id, value, persistValue, persisted);
279     }
280 
281     public void readSettingsEnd() {
282         Object frame = spdySettingsFrame;
283         spdySettingsFrame = null;
284         Channels.fireMessageReceived(ctx, frame);
285     }
286 
287     public void readPingFrame(int id) {
288         SpdyPingFrame spdyPingFrame = new DefaultSpdyPingFrame(id);
289         Channels.fireMessageReceived(ctx, spdyPingFrame);
290     }
291 
292     public void readGoAwayFrame(int lastGoodStreamId, int statusCode) {
293         SpdyGoAwayFrame spdyGoAwayFrame = new DefaultSpdyGoAwayFrame(lastGoodStreamId, statusCode);
294         Channels.fireMessageReceived(ctx, spdyGoAwayFrame);
295     }
296 
297     public void readHeadersFrame(int streamId, boolean last) {
298         spdyHeadersFrame = new DefaultSpdyHeadersFrame(streamId);
299         spdyHeadersFrame.setLast(last);
300     }
301 
302     public void readWindowUpdateFrame(int streamId, int deltaWindowSize) {
303         SpdyWindowUpdateFrame spdyWindowUpdateFrame = new DefaultSpdyWindowUpdateFrame(streamId, deltaWindowSize);
304         Channels.fireMessageReceived(ctx, spdyWindowUpdateFrame);
305     }
306 
307     public void readHeaderBlock(ChannelBuffer headerBlock) {
308         try {
309             spdyHeaderBlockDecoder.decode(headerBlock, spdyHeadersFrame);
310         } catch (Exception e) {
311             Channels.fireExceptionCaught(ctx, e);
312         }
313     }
314 
315     public void readHeaderBlockEnd() {
316         Object frame = null;
317         try {
318             spdyHeaderBlockDecoder.endHeaderBlock(spdyHeadersFrame);
319             frame = spdyHeadersFrame;
320             spdyHeadersFrame = null;
321         } catch (Exception e) {
322             Channels.fireExceptionCaught(ctx, e);
323         }
324         if (frame != null) {
325             Channels.fireMessageReceived(ctx, frame);
326         }
327     }
328 
329     public void readFrameError(String message) {
330         Channels.fireExceptionCaught(ctx, INVALID_FRAME);
331     }
332 }