/* * Copyright (c) 2017 WSO2 Inc. (http://wso2.com) All Rights Reserved. * * Licensed 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; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; 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.codec.http.HttpServerCodec; import io.netty.handler.codec.http.HttpServerUpgradeHandler; import io.netty.handler.codec.http2.Http2CodecUtil; import io.netty.handler.codec.http2.Http2ServerUpgradeCodec; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslHandler; import io.netty.handler.stream.ChunkedWriteHandler; import io.netty.util.AsciiString; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wso2.carbon.messaging.CarbonTransportInitializer; import org.wso2.carbon.transport.http.netty.common.ssl.SSLHandlerFactory; 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.listener.http2.HTTP2SourceHandlerBuilder; import org.wso2.carbon.transport.http.netty.listener.http2.HTTPProtocolNegotiationHandler; import org.wso2.carbon.transport.http.netty.sender.channel.pool.ConnectionManager; import java.util.HashMap; import java.util.Map; /** * A class that responsible for create server side channels. */ public class HTTPServerChannelInitializer extends ChannelInitializer<SocketChannel> implements CarbonTransportInitializer { private static final Logger log = LoggerFactory.getLogger(HTTPServerChannelInitializer.class); private ConnectionManager connectionManager; private Map<Integer, ListenerConfiguration> listenerConfigurationMap = new HashMap<>(); private Map<Integer, SslContext> sslContextMap = new HashMap<>(); public HTTPServerChannelInitializer() { } public void registerListenerConfig(ListenerConfiguration listenerConfiguration, SslContext sslContext) { listenerConfigurationMap.put(listenerConfiguration.getPort(), listenerConfiguration); sslContextMap.put(listenerConfiguration.getPort(), sslContext); } public void unRegisterListenerConfig(ListenerConfiguration listenerConfiguration) { listenerConfigurationMap.remove(listenerConfiguration.getPort()); sslContextMap.remove(listenerConfiguration.getPort()); } @Override public void setup(Map<String, String> parameters) { } public void setupConnectionManager(Map<String, Object> transportProperties) { try { connectionManager = ConnectionManager.getInstance(transportProperties); } catch (Exception e) { log.error("Error initializing the transport ", e); throw new RuntimeException(e); } } @Override public void initChannel(SocketChannel ch) throws Exception { if (log.isDebugEnabled()) { log.debug("Initializing source channel pipeline"); } int port = ch.localAddress().getPort(); ListenerConfiguration listenerConfiguration = listenerConfigurationMap.get(port); ChannelPipeline p = ch.pipeline(); /* * HTTP2 required ALPN Protocol support. Server is required to have ALPN protocol libraries at class path * If http2 in enabled , Assume required libraries available and HTTP/1 and HTTP2 requests will be handled. */ if (listenerConfiguration.isHttp2()) { SslContext sslContext = sslContextMap.get(port); if (sslContextMap.get(port) != null) { /* * In HTTP2 we have two upgrade handlers for HTTPS and HTTP * HTTP - Default upgrade handler * HTTPS - Upgrade during SSL Handshake using Application Layer Protocol negotiation . */ // Configure Upgrade handler for HTTP/2 requests Over TLS configureHttp2TLSPipeline(ch, listenerConfiguration, sslContext); } else { // Configure Upgrade handler for HTTP/2 requests configureHttp2Pipeline(ch, listenerConfiguration); } } else { // Configure Pipeline to handle HTTP/1 requests if HTTP/2 not enabled. if (listenerConfiguration.getSslConfig() != null) { SslHandler sslHandler = new SSLHandlerFactory(listenerConfiguration.getSslConfig()).create(); ch.pipeline().addLast("ssl", sslHandler); } p.addLast("encoder", new HttpResponseEncoder()); configureHTTPPipeline(ch, listenerConfiguration); } } /** * Configure the pipeline for TLS NPN negotiation to HTTP/2. * * @param ch Channel * @param listenerConfiguration Listener Configuration * @param sslContext Netty http2 ALPN SSL context */ private void configureHttp2TLSPipeline(SocketChannel ch, ListenerConfiguration listenerConfiguration, SslContext sslContext) { ChannelPipeline p = ch.pipeline(); p.addLast("ssl", sslContext.newHandler(ch.alloc())); p.addLast("http-upgrade", new HTTPProtocolNegotiationHandler(connectionManager, listenerConfiguration)); } /** * Configure the pipeline for a cleartext upgrade from HTTP to HTTP/2.0 * * @param ch Channel * @param listenerConfiguration Listener Configuration */ private void configureHttp2Pipeline(SocketChannel ch, ListenerConfiguration listenerConfiguration) { ChannelPipeline p = ch.pipeline(); // Add http2 upgrade decoder and upgrade handler for check http version final HttpServerCodec sourceCodec = new HttpServerCodec(); final HttpServerUpgradeHandler.UpgradeCodecFactory upgradeCodecFactory = protocol -> { if (AsciiString.contentEquals(Http2CodecUtil.HTTP_UPGRADE_PROTOCOL_NAME, protocol)) { return new Http2ServerUpgradeCodec("http2-handler", new HTTP2SourceHandlerBuilder(connectionManager, listenerConfiguration).build()); } else { return null; } }; p.addLast("encoder", sourceCodec); p.addLast("http2-upgrade", new HttpServerUpgradeHandler(sourceCodec, upgradeCodecFactory)); /** * Requests will be propagated to next handlers if no upgrade has been attempted and the client is just * talking HTTP. */ configureHTTPPipeline(ch, listenerConfiguration); } /** * Configure the pipeline if user sent HTTP requests * * @param ch Channel * @param listenerConfiguration Listener Configuration */ public void configureHTTPPipeline(SocketChannel ch, ListenerConfiguration listenerConfiguration) { ChannelPipeline p = ch.pipeline(); // Removed the default encoder since http/2 version upgrade already added to pipeline 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); } } @Override public boolean isServerInitializer() { return true; } }