/* * Acceptor.java October 2002 * * Copyright (C) 2002, Niall Gallagher <niallg@users.sf.net> * * 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.simpleframework.transport.connect; import static org.simpleframework.transport.connect.ConnectionEvent.ACCEPT; import static org.simpleframework.transport.connect.ConnectionEvent.ERROR; import java.io.IOException; import java.net.ServerSocket; import java.net.SocketAddress; import java.nio.channels.SelectableChannel; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import org.simpleframework.transport.Server; import org.simpleframework.transport.Socket; import org.simpleframework.transport.SocketWrapper; import org.simpleframework.transport.reactor.Operation; import org.simpleframework.transport.trace.Agent; import org.simpleframework.transport.trace.Trace; /** * The <code>SocketAcceptor</code> object is used to accept incoming TCP * connections from a specified socket address. This is used by the * <code>Connection</code> object as a background process to accept the * connections and hand them to a <code>Server</code>. * <p> * This is capable of processing SSL connections created by the internal server * socket. All SSL connections are forced to finish the SSL handshake before * being dispatched to the server. This ensures that there are no problems with * reading the request. * * @author Niall Gallagher * * @see org.simpleframework.transport.connect.Connection */ class SocketAcceptor implements Operation { /** * This is the server socket channel used to accept connections. */ private final ServerSocketChannel listener; /** * This is the server socket to bind the socket address to. */ private final ServerSocket socket; /** * If provided the SSL context is used to create SSL engines. */ private final SSLContext context; /** * The handler that manages the incoming HTTP connections. */ private final Server server; /** * This is the tracing agent used to trace accepted sockets. */ private final Agent agent; /** * Constructor for the <code>SocketAcceptor</code> object. This accepts new * TCP connections from the specified server socket. Each of the connections * that is accepted is configured for performance for HTTP applications. * * @param address * this is the address to accept connections from * @param context * this is the SSL context used for secure HTTPS * @param server * this is used to initiate the HTTP processing * @param agent * this is the tracing agent associated with this */ public SocketAcceptor(SocketAddress address, SSLContext context, Server server, Agent agent) throws IOException { this.listener = ServerSocketChannel.open(); this.socket = this.listener.socket(); this.context = context; this.agent = agent; this.server = server; this.bind(address); } /** * This is used to acquire the local socket address that this is listening * to. This required in case the socket address that is specified is an * emphemeral address, that is an address that is assigned dynamically when * a port of 0 is specified. * * @return this returns the address for the listening address */ public SocketAddress getAddress() { return this.socket.getLocalSocketAddress(); } /** * This is the <code>SelectableChannel</code> which is used to determine if * the operation should be executed. If the channel is ready for a given I/O * event it can be run. For instance if the operation is used to perform * some form of read operation it can be executed when ready to read data * from the channel. * * @return this returns the channel used to govern execution */ @Override public SelectableChannel getChannel() { return this.listener; } /** * This is used to accept a new TCP connections. When the socket is ready to * accept a connection this method is invoked. It will then create a HTTP * pipeline object using the accepted socket and if provided with an * <code>SSLContext</code> it will also provide an <code>SSLEngine</code> * which is handed to the processor to handle the HTTP requests. */ @Override public void run() { try { this.accept(); } catch (Exception e) { this.pause(); } } /** * This is used to throttle the acceptor when there is an error such as * exhaustion of file descriptors. This will prevent the CPU from being * hogged by the acceptor on such occasions. If the thread can not be put to * sleep then this will freeze. */ private void pause() { try { Thread.sleep(10); } catch (Exception e) { return; } } /** * This is used to cancel the operation if the reactor decides to reject it * for some reason. Typically this method will never be invoked as this * operation never times out. However, should the reactor cancel the * operation this will close the socket. */ @Override public void cancel() { try { this.close(); } catch (Throwable e) { return; } } /** * This is used to configure the server socket for non-blocking mode. It * will also bind the server socket to the socket port specified in the * <code>SocketAddress</code> object. Once done the acceptor is ready to * accept newly arriving connections. * * @param address * this is the server socket address to bind to */ private void bind(SocketAddress address) throws IOException { this.listener.configureBlocking(false); this.socket.setReuseAddress(true); this.socket.bind(address, 100); } /** * The main processing done by this object is done using a thread calling * the <code>run</code> method. Here the TCP connections are accepted from * the <code>ServerSocketChannel</code> which creates the socket objects. * Each socket is then encapsulated in to a pipeline and dispatched to the * processor for processing. * * @throws IOException * if there is a problem accepting the socket */ private void accept() throws IOException { SocketChannel channel = this.listener.accept(); while (channel != null) { Trace trace = this.agent.attach(channel); this.configure(channel); if (this.context == null) { this.process(channel, trace, null); } else { this.process(channel, trace); } channel = this.listener.accept(); } } /** * This method is used to configure the accepted channel. This will disable * Nagles algorithm to improve the performance of the channel, also this * will ensure the accepted channel disables blocking to ensure that it * works within the processor object. * * @param channel * this is the channel that is to be configured */ private void configure(SocketChannel channel) throws IOException { channel.socket().setTcpNoDelay(true); channel.configureBlocking(false); } /** * This method is used to dispatch the socket for processing. The socket * will be configured and connected to the client, this will hand processing * to the <code>Server</code> which will create the pipeline instance used * to wrap the socket object. * * @param channel * this is the connected socket to be processed * @param trace * this is the trace to associate with the socket */ private void process(SocketChannel channel, Trace trace) throws IOException { SSLEngine engine = this.context.createSSLEngine(); try { this.process(channel, trace, engine); } catch (Exception cause) { trace.trace(ERROR, cause); channel.close(); } } /** * This method is used to dispatch the socket for processing. The socket * will be configured and connected to the client, this will hand processing * to the <code>Server</code> which will create the pipeline instance used * to wrap the socket object. * * @param channel * this is the connected socket to be processed * @param trace * this is the trace to associate with the socket * @param engine * this is the SSL engine used for secure HTTPS */ private void process(SocketChannel channel, Trace trace, SSLEngine engine) throws IOException { Socket socket = new SocketWrapper(channel, trace, engine); try { trace.trace(ACCEPT); this.server.process(socket); } catch (Exception cause) { trace.trace(ERROR, cause); channel.close(); } } /** * This is used to close the server socket channel so that the port that it * is bound to is released. This allows the acceptor to close off the * interface to the server. Ensuring the socket is closed allows it to be * recreated at a later point. * * @throws IOException * thrown if the socket can not be closed */ public void close() throws IOException { this.listener.close(); } }