/*
* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. 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 org.wso2.carbon.transport.http.netty.listener.http2;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.http.HttpContentCompressor;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.ssl.ApplicationProtocolNames;
import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.carbon.transport.http.netty.config.ListenerConfiguration;
import org.wso2.carbon.transport.http.netty.config.RequestSizeValidationConfiguration;
import org.wso2.carbon.transport.http.netty.internal.HTTPTransportContextHolder;
import org.wso2.carbon.transport.http.netty.listener.CustomHttpObjectAggregator;
import org.wso2.carbon.transport.http.netty.listener.CustomHttpRequestDecoder;
import org.wso2.carbon.transport.http.netty.listener.SourceHandler;
import org.wso2.carbon.transport.http.netty.sender.channel.pool.ConnectionManager;
/**
* {@code HTTPProtocolNegotiationHandler} negotiates with the client if HTTP2 or HTTP is going to be used. Once
* decided, the Netty pipeline is setup with the correct handlers for the selected protocol.
*/
public class HTTPProtocolNegotiationHandler extends ApplicationProtocolNegotiationHandler {
private static final Logger log = LoggerFactory.getLogger(HTTPProtocolNegotiationHandler.class);
protected ConnectionManager connectionManager;
protected ListenerConfiguration listenerConfiguration;
public HTTPProtocolNegotiationHandler(ConnectionManager connectionManager, ListenerConfiguration
listenerConfiguration) {
super(ApplicationProtocolNames.HTTP_1_1);
this.listenerConfiguration = listenerConfiguration;
this.connectionManager = connectionManager;
}
@Override
/**
* Configure pipeline after SSL handshake
*/
protected void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception {
ChannelPipeline p = ctx.pipeline();
// handles pipeline for HTTP/2 requests after SSL handshake
if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
ctx.pipeline().addLast("http2-handler", new HTTP2SourceHandlerBuilder(connectionManager,
listenerConfiguration).build());
return;
}
// handles pipeline for HTTP/1 requests after SSL handshake
if (ApplicationProtocolNames.HTTP_1_1.equals(protocol)) {
p.addLast("encoder", new HttpResponseEncoder());
if (RequestSizeValidationConfiguration.getInstance().isHeaderSizeValidation()) {
p.addLast("decoder", new CustomHttpRequestDecoder());
} else {
p.addLast("decoder", new HttpRequestDecoder());
}
if (RequestSizeValidationConfiguration.getInstance().isRequestSizeValidation()) {
p.addLast("custom-aggregator", new CustomHttpObjectAggregator());
}
p.addLast("compressor", new HttpContentCompressor());
p.addLast("chunkWriter", new ChunkedWriteHandler());
try {
p.addLast("handler", new SourceHandler(connectionManager, listenerConfiguration));
} catch (Exception e) {
log.error("Cannot Create SourceHandler ", e);
}
return;
}
throw new IllegalStateException("unknown protocol: " + protocol);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (ctx != null && ctx.channel().isActive()) {
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
}
@Override
public void channelActive(final ChannelHandlerContext ctx) throws Exception {
// Start the server connection Timer
if (HTTPTransportContextHolder.getInstance().getHandlerExecutor() != null) {
HTTPTransportContextHolder.getInstance().getHandlerExecutor()
.executeAtSourceConnectionInitiation(Integer.toString(ctx.hashCode()));
}
}
}