blob: cfe68d0301f3dc19864c00bd1f28b501b199cfb4 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.tomcat.util.net;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLEngine;
import org.apache.juli.logging.Log;
import org.apache.tomcat.util.IntrospectionUtils;
import org.apache.tomcat.util.compat.JreCompat;
import org.apache.tomcat.util.net.AbstractEndpoint.Acceptor.AcceptorState;
import org.apache.tomcat.util.res.StringManager;
import org.apache.tomcat.util.threads.LimitLatch;
import org.apache.tomcat.util.threads.ResizableExecutor;
import org.apache.tomcat.util.threads.TaskQueue;
import org.apache.tomcat.util.threads.TaskThreadFactory;
import org.apache.tomcat.util.threads.ThreadPoolExecutor;
/**
*
* @author Mladen Turk
* @author Remy Maucherat
*/
public abstract class AbstractEndpoint<S> {
// -------------------------------------------------------------- Constants
protected static final String DEFAULT_CIPHERS = "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!kRSA";
protected static final StringManager sm = StringManager.getManager("org.apache.tomcat.util.net.res");
public static interface Handler {
/**
* Different types of socket states to react upon.
*/
public enum SocketState {
// TODO Add a new state to the AsyncStateMachine and remove
// ASYNC_END (if possible)
OPEN, CLOSED, LONG, ASYNC_END, SENDFILE, UPGRADING, UPGRADED
}
/**
* Obtain the GlobalRequestProcessor associated with the handler.
*/
public Object getGlobal();
/**
* Recycle resources associated with the handler.
*/
public void recycle();
}
protected enum BindState {
UNBOUND, BOUND_ON_INIT, BOUND_ON_START
}
public abstract static class Acceptor implements Runnable {
public enum AcceptorState {
NEW, RUNNING, PAUSED, ENDED
}
protected volatile AcceptorState state = AcceptorState.NEW;
public final AcceptorState getState() {
return state;
}
private String threadName;
protected final void setThreadName(final String threadName) {
this.threadName = threadName;
}
protected final String getThreadName() {
return threadName;
}
}
private static final int INITIAL_ERROR_DELAY = 50;
private static final int MAX_ERROR_DELAY = 1600;
/**
* Async timeout thread
*/
protected class AsyncTimeout implements Runnable {
private volatile boolean asyncTimeoutRunning = true;
/**
* The background thread that checks async requests and fires the
* timeout if there has been no activity.
*/
@Override
public void run() {
// Loop until we receive a shutdown command
while (asyncTimeoutRunning) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Ignore
}
long now = System.currentTimeMillis();
for (SocketWrapper<S> socket : waitingRequests) {
long access = socket.getLastAccess();
if (socket.getTimeout() > 0 && (now - access) > socket.getTimeout()) {
// Prevent multiple timeouts
socket.setTimeout(-1);
processSocket(socket, SocketStatus.TIMEOUT, true);
}
}
// Loop if endpoint is paused
while (paused && asyncTimeoutRunning) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Ignore
}
}
}
}
protected void stop() {
asyncTimeoutRunning = false;
}
}
// ----------------------------------------------------------------- Fields
/**
* Running state of the endpoint.
*/
protected volatile boolean running = false;
/**
* Will be set to true whenever the endpoint is paused.
*/
protected volatile boolean paused = false;
/**
* Are we using an internal executor
*/
protected volatile boolean internalExecutor = true;
/**
* counter for nr of connections handled by an endpoint
*/
private volatile LimitLatch connectionLimitLatch = null;
/**
* Socket properties
*/
protected SocketProperties socketProperties = new SocketProperties();
public SocketProperties getSocketProperties() {
return socketProperties;
}
/**
* Threads used to accept new connections and pass them to worker threads.
*/
protected Acceptor[] acceptors;
// ----------------------------------------------------------------- Properties
/**
* Time to wait for the internal executor (if used) to terminate when the
* endpoint is stopped in milliseconds. Defaults to 5000 (5 seconds).
*/
private long executorTerminationTimeoutMillis = 5000;
public long getExecutorTerminationTimeoutMillis() {
return executorTerminationTimeoutMillis;
}
public void setExecutorTerminationTimeoutMillis(
long executorTerminationTimeoutMillis) {
this.executorTerminationTimeoutMillis = executorTerminationTimeoutMillis;
}
/**
* Acceptor thread count.
*/
protected int acceptorThreadCount = 0;
public void setAcceptorThreadCount(int acceptorThreadCount) {
this.acceptorThreadCount = acceptorThreadCount;
}
public int getAcceptorThreadCount() { return acceptorThreadCount; }
/**
* Priority of the acceptor threads.
*/
protected int acceptorThreadPriority = Thread.NORM_PRIORITY;
public void setAcceptorThreadPriority(int acceptorThreadPriority) {
this.acceptorThreadPriority = acceptorThreadPriority;
}
public int getAcceptorThreadPriority() { return acceptorThreadPriority; }
private int maxConnections = 10000;
public void setMaxConnections(int maxCon) {
this.maxConnections = maxCon;
LimitLatch latch = this.connectionLimitLatch;
if (latch != null) {
// Update the latch that enforces this
if (maxCon == -1) {
releaseConnectionLatch();
} else {
latch.setLimit(maxCon);
}
} else if (maxCon > 0) {
initializeConnectionLatch();
}
}
public int getMaxConnections() { return this.maxConnections; }
/**
* Return the current count of connections handled by this endpoint, if the
* connections are counted (which happens when the maximum count of
* connections is limited), or <code>-1</code> if they are not. This
* property is added here so that this value can be inspected through JMX.
* It is visible on "ThreadPool" MBean.
*
* <p>The count is incremented by the Acceptor before it tries to accept a
* new connection. Until the limit is reached and thus the count cannot be
* incremented, this value is more by 1 (the count of acceptors) than the
* actual count of connections that are being served.
*
* @return The count
*/
public long getConnectionCount() {
LimitLatch latch = connectionLimitLatch;
if (latch != null) {
return latch.getCount();
}
return -1;
}
/**
* External Executor based thread pool.
*/
private Executor executor = null;
public void setExecutor(Executor executor) {
this.executor = executor;
this.internalExecutor = (executor == null);
}
public Executor getExecutor() { return executor; }
/**
* Server socket port.
*/
private int port;
public int getPort() { return port; }
public void setPort(int port ) { this.port=port; }
public abstract int getLocalPort();
/**
* Address for the server socket.
*/
private InetAddress address;
public InetAddress getAddress() { return address; }
public void setAddress(InetAddress address) { this.address = address; }
/**
* Allows the server developer to specify the backlog that
* should be used for server sockets. By default, this value
* is 100.
*/
private int backlog = 100;
public void setBacklog(int backlog) { if (backlog > 0) this.backlog = backlog; }
public int getBacklog() { return backlog; }
/**
* Controls when the Endpoint binds the port. <code>true</code>, the default
* binds the port on {@link #init()} and unbinds it on {@link #destroy()}.
* If set to <code>false</code> the port is bound on {@link #start()} and
* unbound on {@link #stop()}.
*/
private boolean bindOnInit = true;
public boolean getBindOnInit() { return bindOnInit; }
public void setBindOnInit(boolean b) { this.bindOnInit = b; }
private BindState bindState = BindState.UNBOUND;
/**
* Keepalive timeout, if not set the soTimeout is used.
*/
private Integer keepAliveTimeout = null;
public int getKeepAliveTimeout() {
if (keepAliveTimeout == null) {
return getSoTimeout();
} else {
return keepAliveTimeout.intValue();
}
}
public void setKeepAliveTimeout(int keepAliveTimeout) {
this.keepAliveTimeout = Integer.valueOf(keepAliveTimeout);
}
/**
* Socket TCP no delay.
*/
public boolean getTcpNoDelay() { return socketProperties.getTcpNoDelay();}
public void setTcpNoDelay(boolean tcpNoDelay) { socketProperties.setTcpNoDelay(tcpNoDelay); }
/**
* Socket linger.
*/
public int getSoLinger() { return socketProperties.getSoLingerTime(); }
public void setSoLinger(int soLinger) {
socketProperties.setSoLingerTime(soLinger);
socketProperties.setSoLingerOn(soLinger>=0);
}
/**
* Socket timeout.
*/
public int getSoTimeout() { return socketProperties.getSoTimeout(); }
public void setSoTimeout(int soTimeout) { socketProperties.setSoTimeout(soTimeout); }
/**
* SSL engine.
*/
private boolean SSLEnabled = false;
public boolean isSSLEnabled() { return SSLEnabled; }
public void setSSLEnabled(boolean SSLEnabled) { this.SSLEnabled = SSLEnabled; }
private int minSpareThreads = 10;
public void setMinSpareThreads(int minSpareThreads) {
this.minSpareThreads = minSpareThreads;
Executor executor = this.executor;
if (internalExecutor && executor instanceof java.util.concurrent.ThreadPoolExecutor) {
// The internal executor should always be an instance of
// j.u.c.ThreadPoolExecutor but it may be null if the endpoint is
// not running.
// This check also avoids various threading issues.
((java.util.concurrent.ThreadPoolExecutor) executor).setCorePoolSize(minSpareThreads);
}
}
public int getMinSpareThreads() {
return Math.min(getMinSpareThreadsInternal(), getMaxThreads());
}
private int getMinSpareThreadsInternal() {
if (internalExecutor) {
return minSpareThreads;
} else {
return -1;
}
}
/**
* Maximum amount of worker threads.
*/
private int maxThreads = 200;
public void setMaxThreads(int maxThreads) {
this.maxThreads = maxThreads;
Executor executor = this.executor;
if (internalExecutor && executor instanceof java.util.concurrent.ThreadPoolExecutor) {
// The internal executor should always be an instance of
// j.u.c.ThreadPoolExecutor but it may be null if the endpoint is
// not running.
// This check also avoids various threading issues.
((java.util.concurrent.ThreadPoolExecutor) executor).setMaximumPoolSize(maxThreads);
}
}
public int getMaxThreads() {
if (internalExecutor) {
return maxThreads;
} else {
return -1;
}
}
protected int getMaxThreadsInternal() {
return maxThreads;
}
public int getMaxThreadsWithExecutor() {
Executor executor = this.executor;
if (internalExecutor) {
return maxThreads;
} else {
if (executor instanceof java.util.concurrent.ThreadPoolExecutor) {
return ((java.util.concurrent.ThreadPoolExecutor) executor).getMaximumPoolSize();
} else if (executor instanceof ResizableExecutor) {
return ((ResizableExecutor) executor).getMaxThreads();
}
return -1;
}
}
/**
* Priority of the worker threads.
*/
protected int threadPriority = Thread.NORM_PRIORITY;
public void setThreadPriority(int threadPriority) {
// Can't change this once the executor has started
this.threadPriority = threadPriority;
}
public int getThreadPriority() {
if (internalExecutor) {
return threadPriority;
} else {
return -1;
}
}
/**
* Max keep alive requests
*/
private int maxKeepAliveRequests=100; // as in Apache HTTPD server
public int getMaxKeepAliveRequests() {
return maxKeepAliveRequests;
}
public void setMaxKeepAliveRequests(int maxKeepAliveRequests) {
this.maxKeepAliveRequests = maxKeepAliveRequests;
}
/**
* The maximum number of headers in a request that are allowed.
* 100 by default. A value of less than 0 means no limit.
*/
private int maxHeaderCount = 100; // as in Apache HTTPD server
public int getMaxHeaderCount() {
return maxHeaderCount;
}
public void setMaxHeaderCount(int maxHeaderCount) {
this.maxHeaderCount = maxHeaderCount;
}
/**
* Name of the thread pool, which will be used for naming child threads.
*/
private String name = "TP";
public void setName(String name) { this.name = name; }
public String getName() { return name; }
/**
* The default is true - the created threads will be
* in daemon mode. If set to false, the control thread
* will not be daemon - and will keep the process alive.
*/
private boolean daemon = true;
public void setDaemon(boolean b) { daemon = b; }
public boolean getDaemon() { return daemon; }
protected abstract boolean getDeferAccept();
/**
* Attributes provide a way for configuration to be passed to sub-components
* without the {@link org.apache.coyote.ProtocolHandler} being aware of the
* properties available on those sub-components. One example of such a
* sub-component is the
* {@link org.apache.tomcat.util.net.ServerSocketFactory}.
*/
protected HashMap<String, Object> attributes = new HashMap<>();
/**
* Generic property setter called when a property for which a specific
* setter already exists within the
* {@link org.apache.coyote.ProtocolHandler} needs to be made available to
* sub-components. The specific setter will call this method to populate the
* attributes.
*/
public void setAttribute(String name, Object value) {
if (getLog().isTraceEnabled()) {
getLog().trace(sm.getString("endpoint.setAttribute", name, value));
}
attributes.put(name, value);
}
/**
* Used by sub-components to retrieve configuration information.
*/
public Object getAttribute(String key) {
Object value = attributes.get(key);
if (getLog().isTraceEnabled()) {
getLog().trace(sm.getString("endpoint.getAttribute", key, value));
}
return value;
}
public boolean setProperty(String name, String value) {
setAttribute(name, value);
final String socketName = "socket.";
try {
if (name.startsWith(socketName)) {
return IntrospectionUtils.setProperty(socketProperties, name.substring(socketName.length()), value);
} else {
return IntrospectionUtils.setProperty(this,name,value,false);
}
}catch ( Exception x ) {
getLog().error("Unable to set attribute \""+name+"\" to \""+value+"\"",x);
return false;
}
}
public String getProperty(String name) {
String value = (String) getAttribute(name);
final String socketName = "socket.";
if (value == null && name.startsWith(socketName)) {
Object result = IntrospectionUtils.getProperty(socketProperties, name.substring(socketName.length()));
if (result != null) {
value = result.toString();
}
}
return value;
}
/**
* Return the amount of threads that are managed by the pool.
*
* @return the amount of threads that are managed by the pool
*/
public int getCurrentThreadCount() {
Executor executor = this.executor;
if (executor != null) {
if (executor instanceof ThreadPoolExecutor) {
return ((ThreadPoolExecutor) executor).getPoolSize();
} else if (executor instanceof ResizableExecutor) {
return ((ResizableExecutor) executor).getPoolSize();
} else {
return -1;
}
} else {
return -2;
}
}
/**
* Return the amount of threads that are in use
*
* @return the amount of threads that are in use
*/
public int getCurrentThreadsBusy() {
Executor executor = this.executor;
if (executor != null) {
if (executor instanceof ThreadPoolExecutor) {
return ((ThreadPoolExecutor) executor).getActiveCount();
} else if (executor instanceof ResizableExecutor) {
return ((ResizableExecutor) executor).getActiveCount();
} else {
return -1;
}
} else {
return -2;
}
}
public boolean isRunning() {
return running;
}
public boolean isPaused() {
return paused;
}
public void createExecutor() {
internalExecutor = true;
TaskQueue taskqueue = new TaskQueue();
TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
taskqueue.setParent( (ThreadPoolExecutor) executor);
}
public void shutdownExecutor() {
Executor executor = this.executor;
if (executor != null && internalExecutor) {
this.executor = null;
if (executor instanceof ThreadPoolExecutor) {
//this is our internal one, so we need to shut it down
ThreadPoolExecutor tpe = (ThreadPoolExecutor) executor;
tpe.shutdownNow();
long timeout = getExecutorTerminationTimeoutMillis();
if (timeout > 0) {
try {
tpe.awaitTermination(timeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
// Ignore
}
if (tpe.isTerminating()) {
getLog().warn(sm.getString("endpoint.warn.executorShutdown", getName()));
}
}
TaskQueue queue = (TaskQueue) tpe.getQueue();
queue.setParent(null);
}
}
}
/**
* Unlock the server socket accept using a bogus connection.
*/
protected void unlockAccept() {
// Only try to unlock the acceptor if it is necessary
boolean unlockRequired = false;
for (Acceptor acceptor : acceptors) {
if (acceptor.getState() == AcceptorState.RUNNING) {
unlockRequired = true;
break;
}
}
if (!unlockRequired) {
return;
}
InetSocketAddress saddr = null;
try {
// Need to create a connection to unlock the accept();
if (address == null) {
saddr = new InetSocketAddress("localhost", getLocalPort());
} else if (address.isAnyLocalAddress()) {
saddr = new InetSocketAddress(getUnlockAddress(address), getLocalPort());
} else {
saddr = new InetSocketAddress(address, getLocalPort());
}
try (java.net.Socket s = new java.net.Socket()) {
int stmo = 2 * 1000;
int utmo = 2 * 1000;
if (getSocketProperties().getSoTimeout() > stmo)
stmo = getSocketProperties().getSoTimeout();
if (getSocketProperties().getUnlockTimeout() > utmo)
utmo = getSocketProperties().getUnlockTimeout();
s.setSoTimeout(stmo);
// TODO Consider hard-coding to s.setSoLinger(true,0)
s.setSoLinger(getSocketProperties().getSoLingerOn(),getSocketProperties().getSoLingerTime());
if (getLog().isDebugEnabled()) {
getLog().debug("About to unlock socket for:"+saddr);
}
s.connect(saddr,utmo);
if (getDeferAccept()) {
/*
* In the case of a deferred accept / accept filters we need to
* send data to wake up the accept. Send OPTIONS * to bypass
* even BSD accept filters. The Acceptor will discard it.
*/
OutputStreamWriter sw;
sw = new OutputStreamWriter(s.getOutputStream(), "ISO-8859-1");
sw.write("OPTIONS * HTTP/1.0\r\n" +
"User-Agent: Tomcat wakeup connection\r\n\r\n");
sw.flush();
}
if (getLog().isDebugEnabled()) {
getLog().debug("Socket unlock completed for:"+saddr);
}
// Wait for upto 1000ms acceptor threads to unlock
long waitLeft = 1000;
for (Acceptor acceptor : acceptors) {
while (waitLeft > 0 &&
acceptor.getState() == AcceptorState.RUNNING) {
Thread.sleep(50);
waitLeft -= 50;
}
}
}
} catch(Exception e) {
if (getLog().isDebugEnabled()) {
getLog().debug(sm.getString("endpoint.debug.unlock", "" + getPort()), e);
}
}
}
private static InetAddress getUnlockAddress(InetAddress localAddress)
throws SocketException, UnknownHostException {
// Need a local address of the same type (IPv4 or IPV6) as the
// configured bind address since the connector may be configured
// to not map between types.
InetAddress loopbackUnlockAddress = null;
InetAddress linkLocalUnlockAddress = null;
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
while (networkInterfaces.hasMoreElements()) {
NetworkInterface networkInterface = networkInterfaces.nextElement();
Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
while (inetAddresses.hasMoreElements()) {
InetAddress inetAddress = inetAddresses.nextElement();
if (localAddress.getAddress().getClass().isAssignableFrom(inetAddress.getClass())) {
if (inetAddress.isLoopbackAddress()) {
if (loopbackUnlockAddress == null) {
loopbackUnlockAddress = inetAddress;
}
} else if (inetAddress.isLinkLocalAddress()) {
if (linkLocalUnlockAddress == null) {
linkLocalUnlockAddress = inetAddress;
}
} else {
// Use a non-link local, non-loop back address by default
return inetAddress;
}
}
}
}
// Prefer loop back over link local since on some platforms (e.g.
// OSX) some link local addresses are not included when listening on
// all local addresses.
if (loopbackUnlockAddress != null) {
return loopbackUnlockAddress;
}
if (linkLocalUnlockAddress != null) {
return linkLocalUnlockAddress;
}
// Fallback
return InetAddress.getByName("localhost");
}
// ---------------------------------------------- Request processing methods
/**
* Process the given SocketWrapper with the given status. Used to trigger
* processing as if the Poller (for those endpoints that have one)
* selected the socket.
*
* @param socketWrapper The socket wrapper to process
* @param socketStatus The input status to the processing
* @param dispatch Should the processing be performed on a new
* container thread
*/
public abstract void processSocket(SocketWrapper<S> socketWrapper,
SocketStatus socketStatus, boolean dispatch);
public void executeNonBlockingDispatches(SocketWrapper<S> socketWrapper) {
/*
* This method is called when non-blocking IO is initiated by defining
* a read and/or write listener in a non-container thread. It is called
* once the non-container thread completes so that the first calls to
* onWritePossible() and/or onDataAvailable() as appropriate are made by
* the container.
*
* Processing the dispatches requires (for BIO and APR/native at least)
* that the socket has been added to the waitingRequests queue. This may
* not have occurred by the time that the non-container thread completes
* triggering the call to this method. Therefore, the coded syncs on the
* SocketWrapper as the container thread that initiated this
* non-container thread holds a lock on the SocketWrapper. The container
* thread will add the socket to the waitingRequests queue before
* releasing the lock on the socketWrapper. Therefore, by obtaining the
* lock on socketWrapper before processing the dispatches, we can be
* sure that the socket has been added to the waitingRequests queue.
*/
synchronized (socketWrapper) {
Iterator<DispatchType> dispatches = socketWrapper.getIteratorAndClearDispatches();
while (dispatches != null && dispatches.hasNext()) {
DispatchType dispatchType = dispatches.next();
processSocket(socketWrapper, dispatchType.getSocketStatus(), false);
}
}
}
// ------------------------------------------------------- Lifecycle methods
/*
* NOTE: There is no maintenance of state or checking for valid transitions
* within this class other than ensuring that bind/unbind are called in the
* right place. It is expected that the calling code will maintain state and
* prevent invalid state transitions.
*/
public abstract void bind() throws Exception;
public abstract void unbind() throws Exception;
public abstract void startInternal() throws Exception;
public abstract void stopInternal() throws Exception;
public final void init() throws Exception {
testServerCipherSuitesOrderSupport();
if (bindOnInit) {
bind();
bindState = BindState.BOUND_ON_INIT;
}
}
protected void testServerCipherSuitesOrderSupport() {
// Only test this feature if the user explicitly requested its use.
if(!"".equals(getUseServerCipherSuitesOrder().trim()) && !JreCompat.isJre8Available()) {
throw new UnsupportedOperationException(
sm.getString("endpoint.jsse.cannotHonorServerCipherOrder"));
}
}
public final void start() throws Exception {
if (bindState == BindState.UNBOUND) {
bind();
bindState = BindState.BOUND_ON_START;
}
startInternal();
}
protected final void startAcceptorThreads() {
int count = getAcceptorThreadCount();
acceptors = new Acceptor[count];
for (int i = 0; i < count; i++) {
acceptors[i] = createAcceptor();
String threadName = getName() + "-Acceptor-" + i;
acceptors[i].setThreadName(threadName);
Thread t = new Thread(acceptors[i], threadName);
t.setPriority(getAcceptorThreadPriority());
t.setDaemon(getDaemon());
t.start();
}
}
/**
* Hook to allow Endpoints to provide a specific Acceptor implementation.
*/
protected abstract Acceptor createAcceptor();
/**
* Pause the endpoint, which will stop it accepting new connections.
*/
public void pause() {
if (running && !paused) {
paused = true;
unlockAccept();
}
}
/**
* Resume the endpoint, which will make it start accepting new connections
* again.
*/
public void resume() {
if (running) {
paused = false;
}
}
public final void stop() throws Exception {
stopInternal();
if (bindState == BindState.BOUND_ON_START) {
unbind();
bindState = BindState.UNBOUND;
}
}
public final void destroy() throws Exception {
if (bindState == BindState.BOUND_ON_INIT) {
unbind();
bindState = BindState.UNBOUND;
}
}
protected abstract Log getLog();
// Flags to indicate optional feature support
// Some of these are always hard-coded, some are hard-coded to false (i.e.
// the endpoint does not support them) and some are configurable.
public abstract boolean getUseSendfile();
public abstract boolean getUseComet();
public abstract boolean getUseCometTimeout();
public abstract boolean getUsePolling();
protected LimitLatch initializeConnectionLatch() {
if (maxConnections==-1) return null;
if (connectionLimitLatch==null) {
connectionLimitLatch = new LimitLatch(getMaxConnections());
}
return connectionLimitLatch;
}
protected void releaseConnectionLatch() {
LimitLatch latch = connectionLimitLatch;
if (latch!=null) latch.releaseAll();
connectionLimitLatch = null;
}
protected void countUpOrAwaitConnection() throws InterruptedException {
if (maxConnections==-1) return;
LimitLatch latch = connectionLimitLatch;
if (latch!=null) latch.countUpOrAwait();
}
protected long countDownConnection() {
if (maxConnections==-1) return -1;
LimitLatch latch = connectionLimitLatch;
if (latch!=null) {
long result = latch.countDown();
if (result<0) {
getLog().warn("Incorrect connection count, multiple socket.close called on the same socket." );
}
return result;
} else return -1;
}
/**
* Provides a common approach for sub-classes to handle exceptions where a
* delay is required to prevent a Thread from entering a tight loop which
* will consume CPU and may also trigger large amounts of logging. For
* example, this can happen with the Acceptor thread if the ulimit for open
* files is reached.
*
* @param currentErrorDelay The current delay being applied on failure
* @return The delay to apply on the next failure
*/
protected int handleExceptionWithDelay(int currentErrorDelay) {
// Don't delay on first exception
if (currentErrorDelay > 0) {
try {
Thread.sleep(currentErrorDelay);
} catch (InterruptedException e) {
// Ignore
}
}
// On subsequent exceptions, start the delay at 50ms, doubling the delay
// on every subsequent exception until the delay reaches 1.6 seconds.
if (currentErrorDelay == 0) {
return INITIAL_ERROR_DELAY;
} else if (currentErrorDelay < MAX_ERROR_DELAY) {
return currentErrorDelay * 2;
} else {
return MAX_ERROR_DELAY;
}
}
// -------------------- SSL related properties --------------------
private String algorithm = KeyManagerFactory.getDefaultAlgorithm();
public String getAlgorithm() { return algorithm;}
public void setAlgorithm(String s ) { this.algorithm = s;}
private String clientAuth = "false";
public String getClientAuth() { return clientAuth;}
public void setClientAuth(String s ) { this.clientAuth = s;}
private String keystoreFile = System.getProperty("user.home")+"/.keystore";
public String getKeystoreFile() { return keystoreFile;}
public void setKeystoreFile(String s ) { keystoreFile = s; }
private String keystorePass = null;
public String getKeystorePass() { return keystorePass;}
public void setKeystorePass(String s ) { this.keystorePass = s;}
private String keystoreType = "JKS";
public String getKeystoreType() { return keystoreType;}
public void setKeystoreType(String s ) { this.keystoreType = s;}
private String keystoreProvider = null;
public String getKeystoreProvider() { return keystoreProvider;}
public void setKeystoreProvider(String s ) { this.keystoreProvider = s;}
private String sslProtocol = Constants.SSL_PROTO_TLS;
public String getSslProtocol() { return sslProtocol;}
public void setSslProtocol(String s) { sslProtocol = s;}
private String ciphers = DEFAULT_CIPHERS;
public String getCiphers() { return ciphers;}
public void setCiphers(String s) {
ciphers = s;
}
/**
* @return The ciphers in use by this Endpoint
*/
public abstract String[] getCiphersUsed();
private String useServerCipherSuitesOrder = "";
public String getUseServerCipherSuitesOrder() { return useServerCipherSuitesOrder;}
public void setUseServerCipherSuitesOrder(String s) { this.useServerCipherSuitesOrder = s;}
private String keyAlias = null;
public String getKeyAlias() { return keyAlias;}
public void setKeyAlias(String s ) { keyAlias = s;}
private String keyPass = null;
public String getKeyPass() { return keyPass;}
public void setKeyPass(String s ) { this.keyPass = s;}
private String truststoreFile = System.getProperty("javax.net.ssl.trustStore");
public String getTruststoreFile() {return truststoreFile;}
public void setTruststoreFile(String s) { truststoreFile = s; }
private String truststorePass =
System.getProperty("javax.net.ssl.trustStorePassword");
public String getTruststorePass() {return truststorePass;}
public void setTruststorePass(String truststorePass) {
this.truststorePass = truststorePass;
}
private String truststoreType =
System.getProperty("javax.net.ssl.trustStoreType");
public String getTruststoreType() {return truststoreType;}
public void setTruststoreType(String truststoreType) {
this.truststoreType = truststoreType;
}
private String truststoreProvider = null;
public String getTruststoreProvider() {return truststoreProvider;}
public void setTruststoreProvider(String truststoreProvider) {
this.truststoreProvider = truststoreProvider;
}
private String truststoreAlgorithm = null;
public String getTruststoreAlgorithm() {return truststoreAlgorithm;}
public void setTruststoreAlgorithm(String truststoreAlgorithm) {
this.truststoreAlgorithm = truststoreAlgorithm;
}
private String trustManagerClassName = null;
public String getTrustManagerClassName() {return trustManagerClassName;}
public void setTrustManagerClassName(String trustManagerClassName) {
this.trustManagerClassName = trustManagerClassName;
}
private String crlFile = null;
public String getCrlFile() {return crlFile;}
public void setCrlFile(String crlFile) {
this.crlFile = crlFile;
}
private String trustMaxCertLength = null;
public String getTrustMaxCertLength() {return trustMaxCertLength;}
public void setTrustMaxCertLength(String trustMaxCertLength) {
this.trustMaxCertLength = trustMaxCertLength;
}
private String sessionCacheSize = null;
public String getSessionCacheSize() { return sessionCacheSize;}
public void setSessionCacheSize(String s) { sessionCacheSize = s;}
private String sessionTimeout = "86400";
public String getSessionTimeout() { return sessionTimeout;}
public void setSessionTimeout(String s) { sessionTimeout = s;}
private String allowUnsafeLegacyRenegotiation = null;
public String getAllowUnsafeLegacyRenegotiation() {
return allowUnsafeLegacyRenegotiation;
}
public void setAllowUnsafeLegacyRenegotiation(String s) {
allowUnsafeLegacyRenegotiation = s;
}
private String[] sslEnabledProtocolsarr = new String[0];
public String[] getSslEnabledProtocolsArray() {
return this.sslEnabledProtocolsarr;
}
public void setSslEnabledProtocols(String s) {
if (s == null) {
this.sslEnabledProtocolsarr = new String[0];
} else {
ArrayList<String> sslEnabledProtocols = new ArrayList<>();
StringTokenizer t = new StringTokenizer(s,",");
while (t.hasMoreTokens()) {
String p = t.nextToken().trim();
if (p.length() > 0) {
sslEnabledProtocols.add(p);
}
}
sslEnabledProtocolsarr = sslEnabledProtocols.toArray(
new String[sslEnabledProtocols.size()]);
}
}
protected final Set<SocketWrapper<S>> waitingRequests = Collections
.newSetFromMap(new ConcurrentHashMap<SocketWrapper<S>, Boolean>());
public void removeWaitingRequest(SocketWrapper<S> socketWrapper) {
waitingRequests.remove(socketWrapper);
}
/**
* Configures SSLEngine to honor cipher suites ordering based upon
* endpoint configuration.
*/
protected void configureUseServerCipherSuitesOrder(SSLEngine engine) {
String useServerCipherSuitesOrderStr = this
.getUseServerCipherSuitesOrder().trim();
// Only use this feature if the user explicitly requested its use.
if(!"".equals(useServerCipherSuitesOrderStr)) {
boolean useServerCipherSuitesOrder =
("true".equalsIgnoreCase(useServerCipherSuitesOrderStr)
|| "yes".equalsIgnoreCase(useServerCipherSuitesOrderStr));
JreCompat.getInstance().setUseServerCipherSuitesOrder(engine,
useServerCipherSuitesOrder);
}
}
/**
* The async timeout thread.
*/
private AsyncTimeout asyncTimeout = null;
public AsyncTimeout getAsyncTimeout() {
return asyncTimeout;
}
public void setAsyncTimeout(AsyncTimeout asyncTimeout) {
this.asyncTimeout = asyncTimeout;
}
}