/*
* Copyright (c) 2013-2014 the original author or authors
*
* 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 io.werval.server.netty;
import java.net.InetSocketAddress;
import io.werval.api.events.ConnectionEvent;
import io.werval.spi.ApplicationSPI;
import io.werval.spi.dev.DevShellSPI;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.group.ChannelGroup;
import io.netty.handler.codec.http.HttpContentDecompressor;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
import static java.util.Locale.US;
import static java.util.concurrent.TimeUnit.SECONDS;
import static io.werval.runtime.ConfigKeys.WERVAL_HTTP_LOG_LOWLEVEL_ENABLED;
import static io.werval.runtime.ConfigKeys.WERVAL_HTTP_LOG_LOWLEVEL_LEVEL;
import static io.werval.runtime.ConfigKeys.WERVAL_HTTP_TIMEOUT_READ;
import static io.werval.runtime.ConfigKeys.WERVAL_HTTP_TIMEOUT_WRITE;
/* package */ class HttpServerChannelInitializer
extends ChannelInitializer<Channel>
{
private final ChannelGroup allChannels;
private final ApplicationSPI app;
private final DevShellSPI devSpi;
/* package */ HttpServerChannelInitializer( ChannelGroup allChannels, ApplicationSPI httpApp, DevShellSPI devSpi )
{
this.allChannels = allChannels;
this.app = httpApp;
this.devSpi = devSpi;
}
@Override
public void initChannel( Channel channel )
{
ChannelPipeline pipeline = channel.pipeline();
// Connection Events
String remoteHostString = ( (InetSocketAddress) channel.remoteAddress() ).getHostString();
app.events().emit( new ConnectionEvent.Opened( remoteHostString ) );
channel.closeFuture().addListener(
future -> app.events().emit( new ConnectionEvent.Closed( remoteHostString ) )
);
if( app.config().bool( WERVAL_HTTP_LOG_LOWLEVEL_ENABLED ) )
{
// Log Netty Bytes
LogLevel level = LogLevel.valueOf(
app.config().string( WERVAL_HTTP_LOG_LOWLEVEL_LEVEL ).toUpperCase( US )
);
pipeline.addLast( "byte-logging", new LoggingHandler( "io.werval.server.netty.LowLevelLogger", level ) );
}
// Read/Write Timeout
long readTimeout = app.config().seconds( WERVAL_HTTP_TIMEOUT_READ );
long writeTimeout = app.config().seconds( WERVAL_HTTP_TIMEOUT_WRITE );
pipeline.addLast( "read-timeout", new ReadTimeoutHandler( readTimeout, SECONDS ) );
pipeline.addLast( "write-timeout", new WriteTimeoutHandler( writeTimeout, SECONDS ) );
// HTTP Decoding / Encoding
// HTTP decoders always generates multiple message objects per a single HTTP message:
//
// 1 * HttpRequest / HttpResponse
// 0 - n * HttpContent
// 1 * LastHttpContent
//
// or a single FullHttpRequest if a handler ask for it
pipeline.addLast( "http-codec", new HttpServerCodec() );
// GZip decompression support
pipeline.addLast( "http-decompressor", new HttpContentDecompressor() );
// Allow to send chunked data
pipeline.addLast( "chunked-write-handler", new ChunkedWriteHandler() );
// Protocol Switching Handler
pipeline.addLast( "subprotocol-switcher", new SubProtocolSwitchHandler( allChannels, app, devSpi ) );
}
}