/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package io.netty.handler.codec.spdy;

import javax.net.ssl.SSLEngine;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundByteHandler;
import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.http.HttpChunkAggregator;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.ssl.SslHandler;

/**
 * {@link ChannelInboundByteHandler} which is responsible to setup the {@link ChannelPipeline} either for
 * HTTP or SPDY. This offers an easy way for users to support both at the same time while not care to
 * much about the low-level details.
 *
 */
public abstract class SpdyOrHttpChooser extends ChannelHandlerAdapter implements ChannelInboundByteHandler {

    public static enum SelectedProtocol {
        SpdyVersion2,
        SpdyVersion3,
        HttpVersion1_1,
        HttpVersion1_0,
        None
    }

    private final int maxSpdyContentLength;
    private final int maxHttpContentLength;

    public SpdyOrHttpChooser(int maxSpdyContentLength, int maxHttpContentLength) {
        this.maxSpdyContentLength = maxSpdyContentLength;
        this.maxHttpContentLength = maxHttpContentLength;
    }

    /**
     * Return the {@link SelectedProtocol} for the {@link SSLEngine}. If its not known yet implementations
     * MUST return {@link SelectedProtocol#None}.
     *
     */
    protected abstract SelectedProtocol getProtocol(SSLEngine engine);

    @Override
    public ByteBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception {
        return ctx.nextInboundByteBuffer();
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        initPipeline(ctx);
        ctx.fireChannelActive();
    }

    @Override
    public void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception {
        initPipeline(ctx);
        ctx.fireInboundBufferUpdated();
    }

    private void initPipeline(ChannelHandlerContext ctx) {
        // Get the SslHandler from the ChannelPipeline so we can obtain the SslEngine from it.
        SslHandler handler = ctx.pipeline().get(SslHandler.class);
        if (handler == null) {
            // SslHandler is needed by SPDY by design.
            throw new IllegalStateException("SslHandler is needed for SPDY");
        }

        ChannelPipeline pipeline = ctx.pipeline();
        SelectedProtocol protocol = getProtocol(handler.getEngine());
        switch (protocol) {
        case None:
            // Not done with choosing the protocol, so just return here for now,
            return;
        case SpdyVersion2:
            addSpdyHandlers(ctx, 2);
            break;
        case SpdyVersion3:
            addSpdyHandlers(ctx, 3);
            break;
        case HttpVersion1_0:
        case HttpVersion1_1:
            addHttpHandlers(ctx);
            break;
        default:
            throw new IllegalStateException("Unknown SelectedProtocol");
        }
        // When we reached here we can remove this handler as its now clear what protocol we want to use
        // from this point on.
        pipeline.remove(this);
    }

    /**
     * Add all {@link ChannelHandler}'s that are needed for SPDY with the given version.
     */
    protected void addSpdyHandlers(ChannelHandlerContext ctx, int version) {
        ChannelPipeline pipeline = ctx.pipeline();
        pipeline.addLast("spdyDecoder", new SpdyFrameDecoder(version));
        pipeline.addLast("spdyEncoder", new SpdyFrameEncoder(version));
        pipeline.addLast("spdySessionHandler", new SpdySessionHandler(version, true));
        pipeline.addLast("spdyHttpEncoder", new SpdyHttpEncoder(version));
        pipeline.addLast("spdyHttpDecoder", new SpdyHttpDecoder(version, maxSpdyContentLength));
        pipeline.addLast("spdyStreamIdHandler", new SpdyHttpResponseStreamIdHandler());
        pipeline.addLast("httpRquestHandler", createHttpRequestHandlerForSpdy());
    }

    /**
     * Add all {@link ChannelHandler}'s that are needed for HTTP.
     */
    protected void addHttpHandlers(ChannelHandlerContext ctx) {
        ChannelPipeline pipeline = ctx.pipeline();
        pipeline.addLast("httpRquestDecoder", new HttpRequestDecoder());
        pipeline.addLast("httpResponseEncoder", new HttpResponseEncoder());
        pipeline.addLast("httpChunkAggregator", new HttpChunkAggregator(maxHttpContentLength));
        pipeline.addLast("httpRquestHandler", createHttpRequestHandlerForHttp());
    }

    /**
     * Create the {@link ChannelInboundHandler} that is responsible for handling the {@link HttpRequest}'s
     * when the {@link SelectedProtocol} was {@link SelectedProtocol#HttpVersion1_0} or
     * {@link SelectedProtocol#HttpVersion1_1}
     */
    protected abstract ChannelInboundHandler createHttpRequestHandlerForHttp();

    /**
     * Create the {@link ChannelInboundHandler} that is responsible for handling the {@link HttpRequest}'s
     * when the {@link SelectedProtocol} was {@link SelectedProtocol#SpdyVersion2} or
     * {@link SelectedProtocol#SpdyVersion3}.
     *
     * Bye default this method will just delecate to {@link #createHttpRequestHandlerForHttp()}, but
     * sub-classes may override this to change the behaviour.
     */
    protected ChannelInboundHandler createHttpRequestHandlerForSpdy() {
        return createHttpRequestHandlerForHttp();
    }
}

@@ -731,16 +731,49 @@
 yteBuf;%0A
+import io.netty.buffer.Unpooled;%0A
 import i
@@ -2475,28 +2475,18 @@
 urn 
-ctx.nextInboundByteB
+Unpooled.b
 uffe
@@ -2531,21 +2531,28 @@
 oid 
-channelActive
+inboundBufferUpdated
 (Cha
@@ -2594,32 +2594,36 @@
 ption %7B%0A        
+if (
 initPipeline(ctx
@@ -2615,33 +2615,35 @@
 nitPipeline(ctx)
-;
+) %7B
 %0A        ctx.fir
@@ -2639,164 +2639,268 @@
     
-ctx.fireChannelActive();
+    ctx.nextInboundByteBuffer().writeBytes(ctx.inboundByteBuffer());%0A
 %0A    
-%7D%0A%0A
     
-@Override%0A    public void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception %7B%0A        initPipeline(ctx);%0A
+    // When we reached here we can remove this handler as its now clear what protocol we want to use%0A            // from this point on.%0A            ctx.pipeline().remove(this);%0A%0A    
     
@@ -2934,16 +2934,27 @@
 dated();
+%0A%0A        %7D
 %0A    %7D%0A%0A
@@ -2965,20 +2965,23 @@
 private 
-void
+boolean
  initPip
@@ -3619,16 +3619,22 @@
   return
+ false
 ;%0A      
@@ -4016,165 +4016,19 @@
     
-// When we reached here we can remove this handler as its now clear what protocol we want to use%0A        // from this point on.%0A        pipeline.remove(this)
+return true
 ;%0A  

Fix SpdyOrHttpChooser