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 javax.net.ssl.SSLEngine;
19  
20  import org.jboss.netty.channel.ChannelEvent;
21  import org.jboss.netty.channel.ChannelHandler;
22  import org.jboss.netty.channel.ChannelHandlerContext;
23  import org.jboss.netty.channel.ChannelPipeline;
24  import org.jboss.netty.channel.ChannelUpstreamHandler;
25  import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
26  import org.jboss.netty.handler.codec.http.HttpRequest;
27  import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
28  import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
29  import org.jboss.netty.handler.ssl.SslHandler;
30  
31  /**
32   * {@link ChannelUpstreamHandler} which is responsible to setup the {@link ChannelPipeline} either for
33   * HTTP or SPDY. This offers an easy way for users to support both at the same time while not care to
34   * much about the low-level details.
35   *
36   */
37  public abstract class SpdyOrHttpChooser implements ChannelUpstreamHandler {
38  
39      public enum SelectedProtocol {
40          SpdyVersion3_1,
41          HttpVersion1_1,
42          HttpVersion1_0,
43          None
44      }
45  
46      private final int maxSpdyContentLength;
47      private final int maxHttpContentLength;
48  
49      protected SpdyOrHttpChooser(int maxSpdyContentLength, int maxHttpContentLength) {
50          this.maxSpdyContentLength = maxSpdyContentLength;
51          this.maxHttpContentLength = maxHttpContentLength;
52      }
53  
54      /**
55       * Return the {@link SelectedProtocol} for the {@link SSLEngine}. If its not known yet implementations
56       * MUST return {@link SelectedProtocol#None}.
57       *
58       */
59      protected abstract SelectedProtocol getProtocol(SSLEngine engine);
60  
61      public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception {
62          // Get the SslHandler from the ChannelPipeline so we can obtain the SslEngine from it.
63          SslHandler handler = ctx.getPipeline().get(SslHandler.class);
64          if (handler == null) {
65              // SslHandler is needed by SPDY by design.
66              throw new IllegalStateException("SslHandler is needed for SPDY");
67          }
68  
69          ChannelPipeline pipeline = ctx.getPipeline();
70          SelectedProtocol protocol = getProtocol(handler.getEngine());
71          switch (protocol) {
72          case None:
73              // Not done with choosing the protocol, so just return here for now,
74              return;
75          case SpdyVersion3_1:
76              addSpdyHandlers(ctx, SpdyVersion.SPDY_3_1);
77              break;
78          case HttpVersion1_0:
79          case HttpVersion1_1:
80              addHttpHandlers(ctx);
81              break;
82          default:
83              throw new IllegalStateException("Unknown SelectedProtocol");
84          }
85          // When we reached here we can remove this handler as its now clear what protocol we want to use
86          // from this point on.
87          pipeline.remove(this);
88          ctx.sendUpstream(e);
89      }
90  
91      /**
92       * Add all {@link ChannelHandler}'s that are needed for SPDY with the given version.
93       */
94      protected void addSpdyHandlers(ChannelHandlerContext ctx, SpdyVersion version) {
95          ChannelPipeline pipeline = ctx.getPipeline();
96          pipeline.addLast("spdyFrameCodec", new SpdyFrameCodec(version));
97          pipeline.addLast("spdySessionHandler", new SpdySessionHandler(version, true));
98          pipeline.addLast("spdyHttpEncoder", new SpdyHttpEncoder(version));
99          pipeline.addLast("spdyHttpDecoder", new SpdyHttpDecoder(version, maxSpdyContentLength));
100         pipeline.addLast("spdyStreamIdHandler", new SpdyHttpResponseStreamIdHandler());
101         pipeline.addLast("httpRequestHandler", createHttpRequestHandlerForSpdy());
102     }
103 
104     /**
105      * Add all {@link ChannelHandler}'s that are needed for HTTP.
106      */
107     protected void addHttpHandlers(ChannelHandlerContext ctx) {
108         ChannelPipeline pipeline = ctx.getPipeline();
109         pipeline.addLast("httpRequestDecoder", new HttpRequestDecoder());
110         pipeline.addLast("httpResponseEncoder", new HttpResponseEncoder());
111         pipeline.addLast("httpChunkAggregator", new HttpChunkAggregator(maxHttpContentLength));
112         pipeline.addLast("httpRequestHandler", createHttpRequestHandlerForHttp());
113     }
114 
115     /**
116      * Create the {@link ChannelUpstreamHandler} that is responsible for handling the {@link HttpRequest}'s
117      * when the {@link SelectedProtocol} was {@link SelectedProtocol#HttpVersion1_0} or
118      * {@link SelectedProtocol#HttpVersion1_1}
119      */
120     protected abstract ChannelUpstreamHandler createHttpRequestHandlerForHttp();
121 
122     /**
123      * Create the {@link ChannelUpstreamHandler} that is responsible for handling the {@link HttpRequest}'s
124      * when the {@link SelectedProtocol} was {@link SelectedProtocol#SpdyVersion3_1}.
125      *
126      * By default this method will just delecate to {@link #createHttpRequestHandlerForHttp()}, but
127      * sub-classes may override this to change the behaviour.
128      */
129     protected ChannelUpstreamHandler createHttpRequestHandlerForSpdy() {
130         return createHttpRequestHandlerForHttp();
131     }
132 }