/* * 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;
/** * @param <S> The type used by the socket wrapper associated with this endpoint. * May be the same as U. * @param <U> The type of the underlying socket used by this endpoint. May be * the same as S. * * @author Mladen Turk * @author Remy Maucherat
*/ publicabstractclass AbstractEndpoint<S,U> {
protectedstaticfinal StringManager sm = StringManager.getManager(AbstractEndpoint.class);
publicinterface Handler<S> {
/** * Different types of socket states to react upon.
*/ enum SocketState { // TODO Add a new state to the AsyncStateMachine and remove // ASYNC_END (if possible)
OPEN, CLOSED, LONG, ASYNC_END, SENDFILE, UPGRADING, UPGRADED, ASYNC_IO, SUSPENDED
}
/** * Process the provided socket with the given current status. * * @param socket The socket to process * @param status The current socket status * * @return The state of the socket after processing
*/
SocketState process(SocketWrapperBase<S> socket,
SocketEvent status);
/** * Obtain the GlobalRequestProcessor associated with the handler. * * @return the GlobalRequestProcessor
*/
Object getGlobal();
/** * Release any resources associated with the given SocketWrapper. * * @param socketWrapper The socketWrapper to release resources for
*/ void release(SocketWrapperBase<S> socketWrapper);
/** * Inform the handler that the endpoint has stopped accepting any new * connections. Typically, the endpoint will be stopped shortly * afterwards but it is possible that the endpoint will be resumed so * the handler should not assume that a stop will follow.
*/ void pause();
/** * Recycle resources associated with the handler.
*/ void recycle();
}
publicstaticlong toTimeout(long timeout) { // Many calls can't do infinite timeout so use Long.MAX_VALUE if timeout is <= 0 return (timeout > 0) ? timeout : Long.MAX_VALUE;
}
/** * Running state of the endpoint.
*/ protectedvolatileboolean running = false;
/** * Will be set to true whenever the endpoint is paused.
*/ protectedvolatileboolean paused = false;
/** * Are we using an internal executor
*/ protectedvolatileboolean internalExecutor = true;
/** * counter for nr of connections handled by an endpoint
*/ privatevolatile LimitLatch connectionLimitLatch = null;
/** * Socket properties
*/ protectedfinal SocketProperties socketProperties = new SocketProperties(); public SocketProperties getSocketProperties() { return socketProperties;
}
/** * Thread used to accept new connections and pass them to worker threads.
*/ protected Acceptor<U> acceptor;
/** * Cache for SocketProcessor objects
*/ protected SynchronizedStack<SocketProcessorBase<S>> processorCache;
private ObjectName oname = null;
/** * Map holding all current connections keyed with the sockets.
*/ protected Map<U, SocketWrapperBase<S>> connections = new ConcurrentHashMap<>();
/** * Get a set with the current open connections. * @return A set with the open socket wrappers
*/ public Set<SocketWrapperBase<S>> getConnections() { returnnew HashSet<>(connections.values());
}
private String defaultSSLHostConfigName = SSLHostConfig.DEFAULT_SSL_HOST_NAME; /** * @return The host name for the default SSL configuration for this endpoint * - always in lower case.
*/ public String getDefaultSSLHostConfigName() { return defaultSSLHostConfigName;
} publicvoid setDefaultSSLHostConfigName(String defaultSSLHostConfigName) { this.defaultSSLHostConfigName = defaultSSLHostConfigName.toLowerCase(Locale.ENGLISH);
}
protected ConcurrentMap<String,SSLHostConfig> sslHostConfigs = new ConcurrentHashMap<>(); /** * Add the given SSL Host configuration. * * @param sslHostConfig The configuration to add * * @throws IllegalArgumentException If the host name is not valid or if a * configuration has already been provided * for that host
*/ publicvoid addSslHostConfig(SSLHostConfig sslHostConfig) throws IllegalArgumentException {
addSslHostConfig(sslHostConfig, false);
} /** * Add the given SSL Host configuration, optionally replacing the existing * configuration for the given host. * * @param sslHostConfig The configuration to add * @param replace If {@code true} replacement of an existing * configuration is permitted, otherwise any such * attempted replacement will trigger an exception * * @throws IllegalArgumentException If the host name is not valid or if a * configuration has already been provided * for that host and replacement is not * allowed
*/ publicvoid addSslHostConfig(SSLHostConfig sslHostConfig, boolean replace) throws IllegalArgumentException {
String key = sslHostConfig.getHostName(); if (key == null || key.length() == 0) { thrownew IllegalArgumentException(sm.getString("endpoint.noSslHostName"));
} if (bindState != BindState.UNBOUND && bindState != BindState.SOCKET_CLOSED_ON_STOP &&
isSSLEnabled()) { try {
createSSLContext(sslHostConfig);
} catch (IllegalArgumentException e) { throw e;
} catch (Exception e) { thrownew IllegalArgumentException(e);
}
} if (replace) {
SSLHostConfig previous = sslHostConfigs.put(key, sslHostConfig); if (previous != null) {
unregisterJmx(sslHostConfig);
}
registerJmx(sslHostConfig);
// Do not release any SSLContexts associated with a replaced // SSLHostConfig. They may still be in used by existing connections // and releasing them would break the connection at best. Let GC // handle the clean up.
} else {
SSLHostConfig duplicate = sslHostConfigs.putIfAbsent(key, sslHostConfig); if (duplicate != null) {
releaseSSLContext(sslHostConfig); thrownew IllegalArgumentException(sm.getString("endpoint.duplicateSslHostName", key));
}
registerJmx(sslHostConfig);
}
} /** * Removes the SSL host configuration for the given host name, if such a * configuration exists. * * @param hostName The host name associated with the SSL host configuration * to remove * * @return The SSL host configuration that was removed, if any
*/ public SSLHostConfig removeSslHostConfig(String hostName) { if (hostName == null) { returnnull;
} // Host names are case insensitive but stored/processed in lower case // internally because they are used as keys in a ConcurrentMap where // keys are compared in a case sensitive manner.
String hostNameLower = hostName.toLowerCase(Locale.ENGLISH); if (hostNameLower.equals(getDefaultSSLHostConfigName())) { thrownew IllegalArgumentException(
sm.getString("endpoint.removeDefaultSslHostConfig", hostName));
}
SSLHostConfig sslHostConfig = sslHostConfigs.remove(hostNameLower);
unregisterJmx(sslHostConfig); return sslHostConfig;
} /** * Re-read the configuration files for the SSL host and replace the existing * SSL configuration with the updated settings. Note this replacement will * happen even if the settings remain unchanged. * * @param hostName The SSL host for which the configuration should be * reloaded. This must match a current SSL host
*/ publicvoid reloadSslHostConfig(String hostName) { // Host names are case insensitive but stored/processed in lower case // internally because they are used as keys in a ConcurrentMap where // keys are compared in a case sensitive manner. // This method can be called via various paths so convert the supplied // host name to lower case here to ensure the conversion occurs whatever // the call path.
SSLHostConfig sslHostConfig = sslHostConfigs.get(hostName.toLowerCase(Locale.ENGLISH)); if (sslHostConfig == null) { thrownew IllegalArgumentException(
sm.getString("endpoint.unknownSslHostName", hostName));
}
addSslHostConfig(sslHostConfig, true);
} /** * Re-read the configuration files for all SSL hosts and replace the * existing SSL configuration with the updated settings. Note this * replacement will happen even if the settings remain unchanged.
*/ publicvoid reloadSslHostConfigs() { for (String hostName : sslHostConfigs.keySet()) {
reloadSslHostConfig(hostName);
}
} public SSLHostConfig[] findSslHostConfigs() { return sslHostConfigs.values().toArray(new SSLHostConfig[0]);
}
/** * Create the SSLContext for the given SSLHostConfig. * * @param sslHostConfig The SSLHostConfig for which the SSLContext should be * created * @throws Exception If the SSLContext cannot be created for the given * SSLHostConfig
*/ protectedabstractvoid createSSLContext(SSLHostConfig sslHostConfig) throws Exception;
protectedvoid destroySsl() throws Exception { if (isSSLEnabled()) { for (SSLHostConfig sslHostConfig : sslHostConfigs.values()) {
releaseSSLContext(sslHostConfig);
}
}
}
/** * Release the SSLContext, if any, associated with the SSLHostConfig. * * @param sslHostConfig The SSLHostConfig for which the SSLContext should be * released
*/ protectedvoid releaseSSLContext(SSLHostConfig sslHostConfig) { for (SSLHostConfigCertificate certificate : sslHostConfig.getCertificates()) { if (certificate.getSslContext() != null) {
SSLContext sslContext = certificate.getSslContext(); if (sslContext != null) {
sslContext.destroy();
}
}
}
}
/** * Look up the SSLHostConfig for the given host name. Lookup order is: * <ol> * <li>exact match</li> * <li>wild card match</li> * <li>default SSLHostConfig</li> * </ol> * * @param sniHostName Host name - must be in lower case * * @return The SSLHostConfig for the given host name.
*/ protected SSLHostConfig getSSLHostConfig(String sniHostName) {
SSLHostConfig result = null;
if (sniHostName != null) { // First choice - direct match
result = sslHostConfigs.get(sniHostName); if (result != null) { return result;
} // Second choice, wildcard match int indexOfDot = sniHostName.indexOf('.'); if (indexOfDot > -1) {
result = sslHostConfigs.get("*" + sniHostName.substring(indexOfDot));
}
}
// Fall-back. Use the default if (result == null) {
result = sslHostConfigs.get(getDefaultSSLHostConfigName());
} if (result == null) { // Should never happen. thrownew IllegalStateException();
} return result;
}
/** * Has the user requested that send file be used where possible?
*/ privateboolean useSendfile = true; publicboolean getUseSendfile() { return useSendfile;
} publicvoid setUseSendfile(boolean useSendfile) { this.useSendfile = useSendfile;
}
/** * Time to wait for the internal executor (if used) to terminate when the * endpoint is stopped in milliseconds. Defaults to 5000 (5 seconds).
*/ privatelong executorTerminationTimeoutMillis = 5000;
/** * 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
*/ publiclong getConnectionCount() {
LimitLatch latch = connectionLimitLatch; if (latch != null) { return latch.getCount();
} return -1;
}
publicint getPortWithOffset() { // Zero is a special case and negative values are invalid int port = getPort(); if (port > 0) { return port + getPortOffset();
} return port;
}
/** * Address for the server socket.
*/ private InetAddress address; public InetAddress getAddress() { return address; } publicvoid setAddress(InetAddress address) { this.address = address; }
/** * Obtain the network address the server socket is bound to. This primarily * exists to enable the correct address to be used when unlocking the server * socket since it removes the guess-work involved if no address is * specifically set. * * @return The network address that the server socket is listening on or * null if the server socket is not currently bound. * * @throws IOException If there is a problem determining the currently bound * socket
*/ protectedabstract InetSocketAddress getLocalAddress() throws IOException;
/** * Allows the server developer to specify the acceptCount (backlog) that * should be used for server sockets. By default, this value * is 100.
*/ privateint acceptCount = 100; publicvoid setAcceptCount(int acceptCount) { if (acceptCount > 0) { this.acceptCount = acceptCount;
} } publicint getAcceptCount() { return acceptCount; }
/** * 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()}.
*/ privateboolean bindOnInit = true; publicboolean getBindOnInit() { return bindOnInit; } publicvoid setBindOnInit(boolean b) { this.bindOnInit = b; } privatevolatile BindState bindState = BindState.UNBOUND; protected BindState getBindState() { return bindState;
}
/** * Keepalive timeout, if not set the soTimeout is used.
*/ private Integer keepAliveTimeout = null; publicint getKeepAliveTimeout() { if (keepAliveTimeout == null) { return getConnectionTimeout();
} else { return keepAliveTimeout.intValue();
}
} publicvoid setKeepAliveTimeout(int keepAliveTimeout) { this.keepAliveTimeout = Integer.valueOf(keepAliveTimeout);
}
/** * Socket TCP no delay. * * @return The current TCP no delay setting for sockets created by this * endpoint
*/ publicboolean getTcpNoDelay() { return socketProperties.getTcpNoDelay();} publicvoid setTcpNoDelay(boolean tcpNoDelay) { socketProperties.setTcpNoDelay(tcpNoDelay); }
/** * Socket linger. * * @return The current socket linger time for sockets created by this * endpoint
*/ publicint getConnectionLinger() { return socketProperties.getSoLingerTime(); } publicvoid setConnectionLinger(int connectionLinger) {
socketProperties.setSoLingerTime(connectionLinger);
socketProperties.setSoLingerOn(connectionLinger>=0);
}
/** * Socket timeout. * * @return The current socket timeout for sockets created by this endpoint
*/ publicint getConnectionTimeout() { return socketProperties.getSoTimeout(); } publicvoid setConnectionTimeout(int soTimeout) { socketProperties.setSoTimeout(soTimeout); }
privateint minSpareThreads = 10; publicvoid setMinSpareThreads(int minSpareThreads) { this.minSpareThreads = minSpareThreads;
Executor executor = this.executor; if (internalExecutor && executor instanceof ThreadPoolExecutor) { // The internal executor should always be an instance of // org.apache.tomcat.util.threads.ThreadPoolExecutor but it may be // null if the endpoint is not running. // This check also avoids various threading issues.
((ThreadPoolExecutor) executor).setCorePoolSize(minSpareThreads);
}
} publicint getMinSpareThreads() { return Math.min(getMinSpareThreadsInternal(), getMaxThreads());
} privateint getMinSpareThreadsInternal() { if (internalExecutor) { return minSpareThreads;
} else { return -1;
}
}
/** * Maximum amount of worker threads.
*/ privateint maxThreads = 200; publicvoid setMaxThreads(int maxThreads) { this.maxThreads = maxThreads;
Executor executor = this.executor; if (internalExecutor && executor instanceof ThreadPoolExecutor) { // The internal executor should always be an instance of // org.apache.tomcat.util.threads.ThreadPoolExecutor but it may be // null if the endpoint is not running. // This check also avoids various threading issues.
((ThreadPoolExecutor) executor).setMaximumPoolSize(maxThreads);
}
} publicint getMaxThreads() { if (internalExecutor) { return maxThreads;
} else { return -1;
}
}
/** * Priority of the worker threads.
*/ protectedint threadPriority = Thread.NORM_PRIORITY; publicvoid setThreadPriority(int threadPriority) { // Can't change this once the executor has started this.threadPriority = threadPriority;
} publicint getThreadPriority() { if (internalExecutor) { return threadPriority;
} else { return -1;
}
}
/** * Max keep alive requests
*/ privateint maxKeepAliveRequests=100; // as in Apache HTTPD server publicint getMaxKeepAliveRequests() { // Disable keep-alive if the server socket is not bound if (bindState.isBound()) { return maxKeepAliveRequests;
} else { return 1;
}
} publicvoid setMaxKeepAliveRequests(int maxKeepAliveRequests) { this.maxKeepAliveRequests = maxKeepAliveRequests;
}
/** * Name of the thread pool, which will be used for naming child threads.
*/ private String name = "TP"; publicvoid setName(String name) { this.name = name; } public String getName() { return name; }
/** * Name of domain to use for JMX registration.
*/ private String domain; publicvoid setDomain(String domain) { this.domain = domain; } public String getDomain() { return domain; }
/** * 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.
*/ privateboolean daemon = true; publicvoid setDaemon(boolean b) { daemon = b; } publicboolean getDaemon() { return daemon; }
/** * Always returns {@code false}. * * @return Always {@code false} * * @deprecated This code will be removed in Tomcat 11 onwards
*/
@Deprecated protectedboolean getDeferAccept() { returnfalse;
}
/** * The default behavior is to identify connectors uniquely with address * and port. However, certain connectors are not using that and need * some other identifier, which then can be used as a replacement. * @return the id
*/ public String getId() { returnnull;
}
/** * 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.
*/ 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. * * @param name Name of property to set * @param value The value to set the property to
*/ publicvoid 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. * * @param key The name of the property for which the value should be * retrieved * * @return The value of the specified property
*/ public Object getAttribute(String key) {
Object value = attributes.get(key); if (getLog().isTraceEnabled()) {
getLog().trace(sm.getString("endpoint.getAttribute", key, value));
} return value;
}
publicboolean 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(sm.getString("endpoint.setAttributeError", name, value), x); returnfalse;
}
} 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
*/ publicint getCurrentThreadCount() {
Executor executor = this.executor; if (executor != null) { if (executor instanceof ThreadPoolExecutor) { return ((ThreadPoolExecutor) executor).getPoolSize();
} elseif (executor instanceof java.util.concurrent.ThreadPoolExecutor) { return ((java.util.concurrent.ThreadPoolExecutor) executor).getPoolSize();
} elseif (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
*/ publicint getCurrentThreadsBusy() {
Executor executor = this.executor; if (executor != null) { if (executor instanceof ThreadPoolExecutor) { return ((ThreadPoolExecutor) executor).getActiveCount();
} elseif (executor instanceof java.util.concurrent.ThreadPoolExecutor) { return ((java.util.concurrent.ThreadPoolExecutor) executor).getActiveCount();
} elseif (executor instanceof ResizableExecutor) { return ((ResizableExecutor) executor).getActiveCount();
} else { return -1;
}
} else { return -2;
}
}
publicboolean isRunning() { return running;
}
publicboolean isPaused() { return paused;
}
publicvoid createExecutor() {
internalExecutor = true; if (getUseVirtualThreads()) {
executor = new VirtualThreadExecutor(getName() + "-virt-");
} else {
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);
}
}
publicvoid 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 acceptor threads using bogus connections.
*/ protectedvoid unlockAccept() { // Only try to unlock the acceptor if it is necessary if (acceptor == null || acceptor.getState() != AcceptorState.RUNNING) { return;
}
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); // Newer MacOS versions (e.g. Ventura 13.2) appear to linger for ~1s on close when linger is disabled. // That causes delays when running the unit tests. Explicitly enableing linger but with a timeout of // zero seconds seems to fix the issue.
s.setSoLinger(true, 0); if (getLog().isDebugEnabled()) {
getLog().debug("About to unlock socket for:" + unlockAddress);
}
s.connect(unlockAddress,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:" + unlockAddress);
}
} // Wait for up to 1000ms acceptor threads to unlock. Particularly // for the unit tests, we want to exit this loop as quickly as // possible. However, we also don't want to trigger excessive CPU // usage if the unlock takes longer than expected. Therefore, we // initially wait for the unlock in a tight loop but if that takes // more than 1ms we start using short sleeps to reduce CPU usage. long startTime = System.nanoTime(); while (startTime + 1_000_000_000 > System.nanoTime() && acceptor.getState() == AcceptorState.RUNNING) { if (startTime + 1_000_000 < System.nanoTime()) { Thread.sleep(1);
}
}
} catch(Throwable t) {
ExceptionUtils.handleThrowable(t); if (getLog().isDebugEnabled()) {
getLog().debug(sm.getString( "endpoint.debug.unlock.fail", String.valueOf(getPortWithOffset())), t);
}
}
}
privatestatic InetSocketAddress getUnlockAddress(InetSocketAddress localAddress) throws SocketException { if (localAddress.getAddress().isAnyLocalAddress()) { // 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;
}
} elseif (inetAddress.isLinkLocalAddress()) { if (linkLocalUnlockAddress == null) {
linkLocalUnlockAddress = inetAddress;
}
} else { // Use a non-link local, non-loop back address by default returnnew InetSocketAddress(inetAddress, localAddress.getPort());
}
}
}
} // 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) { returnnew InetSocketAddress(loopbackUnlockAddress, localAddress.getPort());
} if (linkLocalUnlockAddress != null) { returnnew InetSocketAddress(linkLocalUnlockAddress, localAddress.getPort());
} // Fallback returnnew InetSocketAddress("localhost", localAddress.getPort());
} else { return localAddress;
}
}
/** * 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 event The socket event to be processed * @param dispatch Should the processing be performed on a new * container thread * * @return if processing was triggered successfully
*/ publicboolean processSocket(SocketWrapperBase<S> socketWrapper,
SocketEvent event, boolean dispatch) { try { if (socketWrapper == null) { returnfalse;
}
SocketProcessorBase<S> sc = null; if (processorCache != null) {
sc = processorCache.pop();
} if (sc == null) {
sc = createSocketProcessor(socketWrapper, event);
} else {
sc.reset(socketWrapper, event);
}
Executor executor = getExecutor(); if (dispatch && executor != null) {
executor.execute(sc);
} else {
sc.run();
}
} catch (RejectedExecutionException ree) {
getLog().warn(sm.getString("endpoint.executor.fail", socketWrapper) , ree); returnfalse;
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t); // This means we got an OOM or similar creating a thread, or that // the pool and its queue are full
getLog().error(sm.getString("endpoint.process.fail"), t); returnfalse;
} returntrue;
}
/* * 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.
*/
/** * Pause the endpoint, which will stop it accepting new connections and * unlock the acceptor.
*/ publicvoid pause() { if (running && !paused) {
paused = true;
releaseConnectionLatch();
unlockAccept();
getHandler().pause();
}
}
/** * Resume the endpoint, which will make it start accepting new connections * again.
*/ publicvoid resume() { if (running) {
paused = false;
}
}
protectedvoid countUpOrAwaitConnection() throws InterruptedException { if (maxConnections==-1) { return;
}
LimitLatch latch = connectionLimitLatch; if (latch!=null) {
latch.countUpOrAwait();
}
}
protectedlong countDownConnection() { if (maxConnections==-1) { return -1;
}
LimitLatch latch = connectionLimitLatch; if (latch!=null) { long result = latch.countDown(); if (result<0) {
getLog().warn(sm.getString("endpoint.warn.incorrectConnectionCount"));
} return result;
} else { return -1;
}
}
/** * Close the server socket (to prevent further connections) if the server * socket was originally bound on {@link #start()} (rather than on * {@link #init()}). * * @see #getBindOnInit()
*/ publicfinalvoid closeServerSocketGraceful() { if (bindState == BindState.BOUND_ON_START) { // Stop accepting new connections
acceptor.stop(-1); // Release locks that may be preventing the acceptor from stopping
releaseConnectionLatch();
unlockAccept(); // Signal to any multiplexed protocols (HTTP/2) that they may wish // to stop accepting new streams
getHandler().pause(); // Update the bindState. This has the side-effect of disabling // keep-alive for any in-progress connections
bindState = BindState.SOCKET_CLOSED_ON_STOP; try {
doCloseServerSocket();
} catch (IOException ioe) {
getLog().warn(sm.getString("endpoint.serverSocket.closeFailed", getName()), ioe);
}
}
}
/** * Wait for the client connections to the server to close gracefully. The * method will return when all of the client connections have closed or the * method has been waiting for {@code waitTimeMillis}. * * @param waitMillis The maximum time to wait in milliseconds for the * client connections to close. * * @return The wait time, if any remaining when the method returned
*/ publicfinallong awaitConnectionsClose(long waitMillis) { while (waitMillis > 0 && !connections.isEmpty()) { try { Thread.sleep(50);
waitMillis -= 50;
} catch (InterruptedException e) { Thread.interrupted();
waitMillis = 0;
}
} return waitMillis;
}
/** * Actually close the server socket but don't perform any other clean-up. * * @throws IOException If an error occurs closing the socket
*/ protectedabstractvoid doCloseServerSocket() throws IOException;
protectedabstract U serverSocketAccept() throws Exception;
/** * Close the socket when the connection has to be immediately closed when * an error occurs while configuring the accepted socket or trying to * dispatch it for processing. The wrapper associated with the socket will * be used for the close. * @param socket The newly accepted socket
*/ protectedvoid closeSocket(U socket) {
SocketWrapperBase<S> socketWrapper = connections.get(socket); if (socketWrapper != null) {
socketWrapper.close();
}
}
/** * Close the socket. This is used when the connector is not in a state * which allows processing the socket, or if there was an error which * prevented the allocation of the socket wrapper. * @param socket The newly accepted socket
*/ protectedabstractvoid destroySocket(U socket);
}
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.