All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.barchart.http.server.HttpServer Maven / Gradle / Ivy

There is a newer version: 1.1.6
Show newest version
/**
 * 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