//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.server;

import java.nio.channels.SelectableChannel;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.Connection.Listener;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.SelectorManager;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.annotation.Name;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.component.Container;
import org.eclipse.jetty.util.thread.AutoLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

A Listener that limits the number of Connections.

This listener applies a limit to the number of connections, which when exceeded results in a call to AbstractConnector.setAccepting(boolean) to prevent further connections being received. It can be applied to an entire server or to a specific connector by adding it via Container.addBean(Object)

Usage:

  Server server = new Server();
  server.addBean(new ConnectionLimit(5000,server));
  ...
  server.start();
See Also:
/** * <p>A Listener that limits the number of Connections.</p> * <p>This listener applies a limit to the number of connections, which when * exceeded results in a call to {@link AbstractConnector#setAccepting(boolean)} * to prevent further connections being received. It can be applied to an * entire server or to a specific connector by adding it via {@link Container#addBean(Object)} * </p> * <p> * <b>Usage:</b> * </p> * <pre> * Server server = new Server(); * server.addBean(new ConnectionLimit(5000,server)); * ... * server.start(); * </pre> * * @see LowResourceMonitor * @see Connection.Listener * @see SelectorManager.AcceptListener */
@ManagedObject public class ConnectionLimit extends AbstractLifeCycle implements Listener, SelectorManager.AcceptListener { private static final Logger LOG = LoggerFactory.getLogger(ConnectionLimit.class); private final AutoLock _lock = new AutoLock(); private final Server _server; private final List<AbstractConnector> _connectors = new ArrayList<>(); private final Set<SelectableChannel> _accepting = new HashSet<>(); private int _connections; private int _maxConnections; private long _idleTimeout; private boolean _limiting = false; public ConnectionLimit(@Name("maxConnections") int maxConnections, @Name("server") Server server) { _maxConnections = maxConnections; _server = server; } public ConnectionLimit(@Name("maxConnections") int maxConnections, @Name("connectors") Connector... connectors) { this(maxConnections, (Server)null); for (Connector c : connectors) { if (c instanceof AbstractConnector) _connectors.add((AbstractConnector)c); else LOG.warn("Connector {} is not an AbstractConnection. Connections not limited", c); } }
Returns:If >= 0, the endpoint idle timeout in ms to apply when the connection limit is reached
/** * @return If &gt;= 0, the endpoint idle timeout in ms to apply when the connection limit is reached */
@ManagedAttribute("The endpoint idle timeout in ms to apply when the connection limit is reached") public long getIdleTimeout() { return _idleTimeout; }
Params:
  • idleTimeout – If >= 0 the endpoint idle timeout in ms to apply when the connection limit is reached
/** * @param idleTimeout If &gt;= 0 the endpoint idle timeout in ms to apply when the connection limit is reached */
public void setIdleTimeout(long idleTimeout) { _idleTimeout = idleTimeout; } @ManagedAttribute("The maximum number of connections allowed") public int getMaxConnections() { try (AutoLock l = _lock.lock()) { return _maxConnections; } } public void setMaxConnections(int max) { try (AutoLock l = _lock.lock()) { _maxConnections = max; } } @ManagedAttribute("The current number of connections ") public int getConnections() { try (AutoLock l = _lock.lock()) { return _connections; } } @Override protected void doStart() throws Exception { try (AutoLock l = _lock.lock()) { if (_server != null) { for (Connector c : _server.getConnectors()) { if (c instanceof AbstractConnector) _connectors.add((AbstractConnector)c); else LOG.warn("Connector {} is not an AbstractConnector. Connections not limited", c); } } if (LOG.isDebugEnabled()) LOG.debug("ConnectionLimit {} for {}", _maxConnections, _connectors); _connections = 0; _limiting = false; for (AbstractConnector c : _connectors) { c.addBean(this); } } } @Override protected void doStop() throws Exception { try (AutoLock l = _lock.lock()) { for (AbstractConnector c : _connectors) { c.removeBean(this); } _connections = 0; if (_server != null) _connectors.clear(); } } protected void check() { if ((_accepting.size() + _connections) >= _maxConnections) { if (!_limiting) { _limiting = true; LOG.info("Connection Limit({}) reached for {}", _maxConnections, _connectors); limit(); } } else { if (_limiting) { _limiting = false; LOG.info("Connection Limit({}) cleared for {}", _maxConnections, _connectors); unlimit(); } } } protected void limit() { for (AbstractConnector c : _connectors) { c.setAccepting(false); if (_idleTimeout > 0) { for (EndPoint endPoint : c.getConnectedEndPoints()) { endPoint.setIdleTimeout(_idleTimeout); } } } } protected void unlimit() { for (AbstractConnector c : _connectors) { c.setAccepting(true); if (_idleTimeout > 0) { for (EndPoint endPoint : c.getConnectedEndPoints()) { endPoint.setIdleTimeout(c.getIdleTimeout()); } } } } @Override public void onAccepting(SelectableChannel channel) { try (AutoLock l = _lock.lock()) { _accepting.add(channel); if (LOG.isDebugEnabled()) LOG.debug("onAccepting ({}+{}) < {} {}", _accepting.size(), _connections, _maxConnections, channel); check(); } } @Override public void onAcceptFailed(SelectableChannel channel, Throwable cause) { try (AutoLock l = _lock.lock()) { _accepting.remove(channel); if (LOG.isDebugEnabled()) LOG.debug("onAcceptFailed ({}+{}) < {} {} {}", _accepting.size(), _connections, _maxConnections, channel, cause); check(); } } @Override public void onAccepted(SelectableChannel channel) { } @Override public void onOpened(Connection connection) { try (AutoLock l = _lock.lock()) { _accepting.remove(connection.getEndPoint().getTransport()); _connections++; if (LOG.isDebugEnabled()) LOG.debug("onOpened ({}+{}) < {} {}", _accepting.size(), _connections, _maxConnections, connection); check(); } } @Override public void onClosed(Connection connection) { try (AutoLock l = _lock.lock()) { _connections--; if (LOG.isDebugEnabled()) LOG.debug("onClosed ({}+{}) < {} {}", _accepting.size(), _connections, _maxConnections, connection); check(); } } }