protectedstaticfinal StringManager sm = StringManager.getManager(AbstractEndpoint.class);
publicinterface Handler<S> {
/** *Differenttypesofsocketstatestoreactupon.
*/ 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
}
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;
}
// 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);
}
} /** *RemovestheSSLhostconfigurationforthegivenhostname,ifsucha *configurationexists. * *@paramhostNameThehostnameassociatedwiththeSSLhostconfiguration *toremove * *@returnTheSSLhostconfigurationthatwasremoved,ifany
*/ 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-readtheconfigurationfilesfortheSSLhostandreplacetheexisting *SSLconfigurationwiththeupdatedsettings.Notethisreplacementwill *happenevenifthesettingsremainunchanged. * *@paramhostNameTheSSLhostforwhichtheconfigurationshouldbe *reloaded.ThismustmatchacurrentSSLhost
*/ 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-readtheconfigurationfilesforallSSLhostsandreplacethe *existingSSLconfigurationwiththeupdatedsettings.Notethis *replacementwillhappenevenifthesettingsremainunchanged.
*/ publicvoid reloadSslHostConfigs() { for (String hostName : sslHostConfigs.keySet()) {
reloadSslHostConfig(hostName);
}
} public SSLHostConfig[] findSslHostConfigs() { return sslHostConfigs.values().toArray(new SSLHostConfig[0]);
}
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;
}
publicint getPortWithOffset() { // Zero is a special case and negative values are invalid int port = getPort(); if (port > 0) { return port + getPortOffset();
} return port;
}
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;
}
}
/** *Maximumamountofworkerthreads.
*/ 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;
}
}
/** *Priorityoftheworkerthreads.
*/ 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;
}
}
/** *Maxkeepaliverequests
*/ 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 { return1;
}
} publicvoid setMaxKeepAliveRequests(int maxKeepAliveRequests) { this.maxKeepAliveRequests = maxKeepAliveRequests;
}
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);
}
}
}
/** *Unlocktheserversocketacceptorthreadsusingbogusconnections.
*/ 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()) { /* *Inthecaseofadeferredaccept/acceptfiltersweneedto *senddatatowakeuptheaccept.SendOPTIONS*tobypass *evenBSDacceptfilters.TheAcceptorwilldiscardit.
*/
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;
}
}
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;
}
}
/** *Closetheserversocket(topreventfurtherconnections)iftheserver *socketwasoriginallyboundon{@link#start()}(ratherthanon *{@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);
}
}
}
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 und die Messung sind noch experimentell.