com.barchart.http.server.HttpServer Maven / Gradle / Ivy
/**
* Copyright (C) 2011-2013 Barchart, Inc.
*
* All rights reserved. Licensed under the OSI BSD License.
*
* http://www.opensource.org/licenses/bsd-license.php
*/
package com.barchart.http.server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelStateHandlerAdapter;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.ChannelGroupFuture;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.stream.ChunkedWriteHandler;
/**
* High performance HTTP server.
*/
public class HttpServer {
private Channel serverChannel;
private HttpServerConfig config;
private HttpRequestChannelHandler channelHandler;
private ConnectionTracker clientTracker;
private final ChannelGroup channelGroup = new DefaultChannelGroup();
public HttpServer configure(final HttpServerConfig config_) {
config = config_;
channelHandler = new HttpRequestChannelHandler(config);
clientTracker = new ConnectionTracker(config.maxConnections());
return this;
}
/**
* Start the server with the configuration settings provided.
*/
public ChannelFuture listen() {
if (config == null) {
throw new IllegalStateException("Server has not been configured");
}
if (serverChannel != null) {
throw new IllegalStateException("Server is already running.");
}
final ChannelFuture future = new ServerBootstrap() //
.group(config.parentGroup(), config.childGroup()) //
.channel(NioServerSocketChannel.class) //
.localAddress(config.address()) //
.childHandler(new HttpServerChannelInitializer()) //
.option(ChannelOption.SO_REUSEADDR, true) //
.option(ChannelOption.SO_SNDBUF, 262144) //
.option(ChannelOption.SO_RCVBUF, 262144) //
.bind();
serverChannel = future.channel();
return future;
}
/**
* Shutdown the server. This does not kill active client connections.
*/
public ChannelFuture shutdown() {
if (serverChannel == null) {
throw new IllegalStateException("Server is not running.");
}
final ChannelFuture future = serverChannel.close();
serverChannel = null;
return future;
}
/**
* Return a future for the server shutdown process.
*/
public ChannelFuture shutdownFuture() {
return serverChannel.closeFuture();
}
/**
* Shutdown the server and kill all active client connections.
*/
public ChannelGroupFuture kill() {
if (serverChannel == null) {
throw new IllegalStateException("Server is not running.");
}
channelGroup.add(serverChannel);
final ChannelGroupFuture future = channelGroup.close();
channelGroup.remove(serverChannel);
serverChannel = null;
return future;
}
public boolean isRunning() {
return serverChannel != null;
}
public HttpServerConfig config() {
return config;
}
private class HttpServerChannelInitializer extends
ChannelInitializer {
@Override
public void initChannel(final SocketChannel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new HttpResponseEncoder(), //
new ChunkedWriteHandler(), //
clientTracker, //
new HttpRequestDecoder(), //
new HttpObjectAggregator(config.maxRequestSize()), //
// new MessageLoggingHandler(LogLevel.INFO), //
channelHandler);
}
}
@Sharable
private class ConnectionTracker extends ChannelStateHandlerAdapter {
private int maxConnections = -1;
public ConnectionTracker(final int connections) {
maxConnections = connections;
}
@Override
public void channelActive(final ChannelHandlerContext context) {
if (maxConnections > -1 && channelGroup.size() >= maxConnections) {
final ByteBuf content = Unpooled.buffer();
content.writeBytes("503 Service Unavailable - Server Too Busy"
.getBytes());
final FullHttpResponse response =
new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
HttpResponseStatus.SERVICE_UNAVAILABLE);
response.headers().set(HttpHeaders.Names.CONTENT_LENGTH,
content.readableBytes());
response.data().writeBytes(content);
context.write(response)
.addListener(ChannelFutureListener.CLOSE);
return;
}
channelGroup.add(context.channel());
context.fireChannelActive();
}
@Override
public void channelInactive(final ChannelHandlerContext context) {
channelGroup.remove(context.channel());
context.fireChannelInactive();
}
@Override
public void inboundBufferUpdated(final ChannelHandlerContext ctx)
throws Exception {
ctx.fireInboundBufferUpdated();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy