package org.caudexorigo.http.netty; import static org.jboss.netty.channel.Channels.pipeline; import java.net.InetSocketAddress; import java.util.concurrent.Executor; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import org.caudexorigo.Shutdown; import org.caudexorigo.concurrent.CustomExecutors; import org.caudexorigo.http.netty.reporting.ResponseFormatter; import org.caudexorigo.http.netty.reporting.StandardResponseFormatter; import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.channel.ChannelFactory; import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.channel.ChannelPipelineFactory; import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; import org.jboss.netty.handler.codec.http.HttpContentCompressor; import org.jboss.netty.handler.codec.http.HttpRequestDecoder; import org.jboss.netty.handler.codec.http.HttpResponseEncoder; import org.jboss.netty.handler.ssl.SslHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class NettyHttpServer { private static final String DEFAULT_HOST = "0.0.0.0"; private static final int DEFAULT_PORT = 8080; private static Logger log = LoggerFactory.getLogger(NettyHttpServer.class); private Executor _bossExecutor; private String _host; private RequestRouter _mapper; private RequestObserver _requestObserver; private ResponseFormatter _rspFmt; private int _port; private Executor _workerExecutor; private final boolean _is_compression_enabled; // private Map<String, WebSocketHandler> _webSocketHandlers = new HashMap<String, WebSocketHandler>(); // private boolean _hasWebSocketSupport; public NettyHttpServer() { this(DEFAULT_HOST, DEFAULT_PORT, false); } public NettyHttpServer(String host, int port) { this(host, port, false); } public NettyHttpServer(String host, int port, boolean is_compression_enabled) { _host = host; _port = port; _is_compression_enabled = is_compression_enabled; } public NettyHttpServer(String host, int port, boolean is_compression_enabled, Executor boss_executor, Executor worker_executor) { this(host, port, is_compression_enabled); _bossExecutor = boss_executor; _workerExecutor = worker_executor; } private synchronized Executor getBossThreadPool() { if (_bossExecutor == null) { _bossExecutor = CustomExecutors.newCachedThreadPool("http-boss"); } return _bossExecutor; } public String getHost() { return _host; } public int getPort() { return _port; } public RequestRouter getRouter() { return _mapper; } private Executor getWorkerThreadPool() { if (_workerExecutor == null) { _workerExecutor = CustomExecutors.newCachedThreadPool("http-worker"); } return _workerExecutor; } public void setHost(String host) { _host = host; } public void setPort(int port) { _port = port; } public void setRouter(RequestRouter mapper) { _mapper = mapper; } private ResponseFormatter getResponseFormtter() { if (_rspFmt != null) { return _rspFmt; } else { return new StandardResponseFormatter(false); } } protected RequestObserver getRequestObserver() { if (_requestObserver != null) { return _requestObserver; } else { return new DefaultObserver(); } } public void setRequestObserver(RequestObserver requestObserver) { _requestObserver = requestObserver; } public void setResponseFormtter(ResponseFormatter rspFmt) { _rspFmt = rspFmt; } // public void addWebSocketHandler(String path, WebSocketHandler webSocketHandler) // { // if (webSocketHandler == null) // { // throw new IllegalArgumentException("WebSocketHandler can not be null"); // } // if (StringUtils.isBlank(path)) // { // throw new IllegalArgumentException("WebSocketHandler Path can not be blank"); // } // // _webSocketHandlers.put(path, webSocketHandler); // _hasWebSocketSupport = true; // } public synchronized void start() { log.info("Starting Httpd"); ChannelFactory factory = new NioServerSocketChannelFactory(getBossThreadPool(), getWorkerThreadPool()); ServerBootstrap bootstrap = new ServerBootstrap(factory); ChannelPipelineFactory pf = new ChannelPipelineFactory() { @Override public ChannelPipeline getPipeline() throws Exception { try { // Create a default pipeline implementation. ChannelPipeline pipeline = pipeline(); // if (_hasWebSocketSupport) // { // pipeline.addLast("policy-file", new PolicyFileRequestDecoder(getPort())); // } pipeline.addLast("http-decoder", new HttpRequestDecoder(4096, 8192, 256 * 1024)); pipeline.addLast("http-encoder", new HttpResponseEncoder()); if (_is_compression_enabled) { pipeline.addLast("http-compression", new HttpContentCompressor()); } // if (_hasWebSocketSupport) // { // Set<String> ws_paths = _webSocketHandlers.keySet(); // // for (String wspath : ws_paths) // { // http_handler.addWebSocketHandler(wspath, _webSocketHandlers.get(wspath)); // } // } pipeline.addLast("http-handler", new HttpProtocolHandler(_mapper, getRequestObserver(), getResponseFormtter())); return pipeline; } catch (Throwable t) { Shutdown.now(t); return null; } } }; setupBootStrap(bootstrap, pf); try { // Bind and start to accept incoming connections. InetSocketAddress inet = new InetSocketAddress(getHost(), getPort()); bootstrap.bind(inet); log.info("Httpd started. Listening on port: {}", inet.toString()); } catch (Throwable t) { throw new RuntimeException(t); } } public synchronized void startSsl(final HttpSslContext http_ssl_ctx) { log.info("Starting Httpd - SSL"); ChannelFactory factory = new NioServerSocketChannelFactory(getBossThreadPool(), getWorkerThreadPool()); ServerBootstrap bootstrap = new ServerBootstrap(factory); final SSLContext sslContext; try { sslContext = http_ssl_ctx.getSSLContext(); } catch (Exception e) { throw new RuntimeException(e); } ChannelPipelineFactory pf = new ChannelPipelineFactory() { @Override public ChannelPipeline getPipeline() throws Exception { try { ChannelPipeline pipeline = pipeline(); final SSLEngine sslEngine = sslContext.createSSLEngine(); sslEngine.setUseClientMode(false); final SslHandler sslHandler = new SslHandler(sslEngine); final HttpRequestDecoder httpRequestDecoder = new HttpRequestDecoder(4096, 8192, 256 * 1024); final HttpResponseEncoder httpResponseEncoder = new HttpResponseEncoder(); final HttpProtocolHandler http_handler = new HttpProtocolHandler(_mapper, getRequestObserver(), getResponseFormtter()); pipeline.addLast("ssl", sslHandler); pipeline.addLast("http-decoder", httpRequestDecoder); pipeline.addLast("http-encoder", httpResponseEncoder); pipeline.addLast("http-handler", http_handler); return pipeline; } catch (Throwable t) { Shutdown.now(t); return null; } } }; setupBootStrap(bootstrap, pf); try { // Bind and start to accept incoming connections. InetSocketAddress inet = new InetSocketAddress(getHost(), http_ssl_ctx.getSslPort()); bootstrap.bind(inet); log.info("Httpd SSL started. Listening on port: {}", inet.toString()); } catch (Throwable t) { throw new RuntimeException(t); } } private void setupBootStrap(ServerBootstrap bootstrap, ChannelPipelineFactory pf) { bootstrap.setPipelineFactory(pf); bootstrap.setOption("child.tcpNoDelay", true); bootstrap.setOption("child.keepAlive", true); bootstrap.setOption("child.receiveBufferSize", 128 * 1024); bootstrap.setOption("child.sendBufferSize", 128 * 1024); bootstrap.setOption("reuseAddress", true); bootstrap.setOption("backlog", 1024); } public void stop() { log.warn("pt.com.http.NettyHttpServer.stop() is not implemented"); } }