X-Git-Url: https://code.wpia.club/?p=gigi.git;a=blobdiff_plain;f=lib%2Fjetty%2Forg%2Feclipse%2Fjetty%2Fserver%2FAbstractConnector.java;fp=lib%2Fjetty%2Forg%2Feclipse%2Fjetty%2Fserver%2FAbstractConnector.java;h=7d3402fbfa77b37fd014a44a92699ff06591333f;hp=0000000000000000000000000000000000000000;hb=73ef54a38e3930a1a789cdc6b5fa23cdd4c9d086;hpb=515007c7c1351045420669d65b59c08fa46850f2 diff --git a/lib/jetty/org/eclipse/jetty/server/AbstractConnector.java b/lib/jetty/org/eclipse/jetty/server/AbstractConnector.java new file mode 100644 index 00000000..7d3402fb --- /dev/null +++ b/lib/jetty/org/eclipse/jetty/server/AbstractConnector.java @@ -0,0 +1,564 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.server; + +import java.io.IOException; +import java.net.Socket; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.io.ArrayByteBufferPool; +import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.ssl.SslConnection; +import org.eclipse.jetty.util.FutureCallback; +import org.eclipse.jetty.util.annotation.ManagedAttribute; +import org.eclipse.jetty.util.annotation.ManagedObject; +import org.eclipse.jetty.util.component.ContainerLifeCycle; +import org.eclipse.jetty.util.component.Dumpable; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler; +import org.eclipse.jetty.util.thread.Scheduler; + +/** + *

An abstract implementation of {@link Connector} that provides a {@link ConnectionFactory} mechanism + * for creating {@link Connection} instances for various protocols (HTTP, SSL, SPDY, etc).

+ * + *

Connector Services

+ * The abstract connector manages the dependent services needed by all specific connector instances: + * + * These services are managed as aggregate beans by the {@link ContainerLifeCycle} super class and + * may either be managed or unmanaged beans. + * + *

Connection Factories

+ * The connector keeps a collection of {@link ConnectionFactory} instances, each of which are known by their + * protocol name. The protocol name may be a real protocol (eg http/1.1 or spdy/3) or it may be a private name + * that represents a special connection factory. For example, the name "SSL-http/1.1" is used for + * an {@link SslConnectionFactory} that has been instantiated with the {@link HttpConnectionFactory} as it's + * next protocol. + * + *

Configuring Connection Factories

+ * The collection of available {@link ConnectionFactory} may be constructor injected or modified with the + * methods {@link #addConnectionFactory(ConnectionFactory)}, {@link #removeConnectionFactory(String)} and + * {@link #setConnectionFactories(Collection)}. Only a single {@link ConnectionFactory} instance may be configured + * per protocol name, so if two factories with the same {@link ConnectionFactory#getProtocol()} are set, then + * the second will replace the first. + *

+ * The protocol factory used for newly accepted connections is specified by + * the method {@link #setDefaultProtocol(String)} or defaults to the protocol of the first configured factory. + *

+ * Each Connection factory type is responsible for the configuration of the protocols that it accepts. Thus to + * configure the HTTP protocol, you pass a {@link HttpConfiguration} instance to the {@link HttpConnectionFactory} + * (or the SPDY factories that can also provide HTTP Semantics). Similarly the {@link SslConnectionFactory} is + * configured by passing it a {@link SslContextFactory} and a next protocol name. + * + *

Connection Factory Operation

+ * {@link ConnectionFactory}s may simply create a {@link Connection} instance to support a specific + * protocol. For example, the {@link HttpConnectionFactory} will create a {@link HttpConnection} instance + * that can handle http/1.1, http/1.0 and http/0.9. + *

+ * {@link ConnectionFactory}s may also create a chain of {@link Connection} instances, using other {@link ConnectionFactory} instances. + * For example, the {@link SslConnectionFactory} is configured with a next protocol name, so that once it has accepted + * a connection and created an {@link SslConnection}, it then used the next {@link ConnectionFactory} from the + * connector using the {@link #getConnectionFactory(String)} method, to create a {@link Connection} instance that + * will handle the unecrypted bytes from the {@link SslConnection}. If the next protocol is "http/1.1", then the + * {@link SslConnectionFactory} will have a protocol name of "SSL-http/1.1" and lookup "http/1.1" for the protocol + * to run over the SSL connection. + *

+ * {@link ConnectionFactory}s may also create temporary {@link Connection} instances that will exchange bytes + * over the connection to determine what is the next protocol to use. For example the NPN protocol is an extension + * of SSL to allow a protocol to be specified during the SSL handshake. NPN is used by the SPDY protocol to + * negotiate the version of SPDY or HTTP that the client and server will speak. Thus to accept a SPDY connection, the + * connector will be configured with {@link ConnectionFactory}s for "SSL-NPN", "NPN", "spdy/3", "spdy/2", "http/1.1" + * with the default protocol being "SSL-NPN". Thus a newly accepted connection uses "SSL-NPN", which specifies a + * SSLConnectionFactory with "NPN" as the next protocol. Thus an SslConnection instance is created chained to an NPNConnection + * instance. The NPN connection then negotiates with the client to determined the next protocol, which could be + * "spdy/3", "spdy/2" or the default of "http/1.1". Once the next protocol is determined, the NPN connection + * calls {@link #getConnectionFactory(String)} to create a connection instance that will replace the NPN connection as + * the connection chained to the SSLConnection. + *

+ *

Acceptors

+ * The connector will execute a number of acceptor tasks to the {@link Exception} service passed to the constructor. + * The acceptor tasks run in a loop while the connector is running and repeatedly call the abstract {@link #accept(int)} method. + * The implementation of the accept method must: + * + *
  • block waiting for new connections + *
  • accept the connection (eg socket accept) + *
  • perform any configuration of the connection (eg. socket linger times) + *
  • call the {@link #getDefaultConnectionFactory()} {@link ConnectionFactory#newConnection(Connector, org.eclipse.jetty.io.EndPoint)} + * method to create a new Connection instance. + * + * The default number of acceptor tasks is the minimum of 1 and half the number of available CPUs. Having more acceptors may reduce + * the latency for servers that see a high rate of new connections (eg HTTP/1.0 without keep-alive). Typically the default is + * sufficient for modern persistent protocols (HTTP/1.1, SPDY etc.) + */ +@ManagedObject("Abstract implementation of the Connector Interface") +public abstract class AbstractConnector extends ContainerLifeCycle implements Connector, Dumpable +{ + protected final Logger LOG = Log.getLogger(getClass()); + // Order is important on server side, so we use a LinkedHashMap + private final Map _factories = new LinkedHashMap<>(); + private final Server _server; + private final Executor _executor; + private final Scheduler _scheduler; + private final ByteBufferPool _byteBufferPool; + private final Thread[] _acceptors; + private final Set _endpoints = Collections.newSetFromMap(new ConcurrentHashMap()); + private final Set _immutableEndPoints = Collections.unmodifiableSet(_endpoints); + private volatile CountDownLatch _stopping; + private long _idleTimeout = 30000; + private String _defaultProtocol; + private ConnectionFactory _defaultConnectionFactory; + private String _name; + + + /** + * @param server The server this connector will be added to. Must not be null. + * @param executor An executor for this connector or null to use the servers executor + * @param scheduler A scheduler for this connector or null to either a {@link Scheduler} set as a server bean or if none set, then a new {@link ScheduledExecutorScheduler} instance. + * @param pool A buffer pool for this connector or null to either a {@link ByteBufferPool} set as a server bean or none set, the new {@link ArrayByteBufferPool} instance. + * @param acceptors the number of acceptor threads to use, or 0 for a default value. + * @param factories The Connection Factories to use. + */ + public AbstractConnector( + Server server, + Executor executor, + Scheduler scheduler, + ByteBufferPool pool, + int acceptors, + ConnectionFactory... factories) + { + _server=server; + _executor=executor!=null?executor:_server.getThreadPool(); + if (scheduler==null) + scheduler=_server.getBean(Scheduler.class); + _scheduler=scheduler!=null?scheduler:new ScheduledExecutorScheduler(); + if (pool==null) + pool=_server.getBean(ByteBufferPool.class); + _byteBufferPool = pool!=null?pool:new ArrayByteBufferPool(); + + addBean(_server,false); + addBean(_executor); + if (executor==null) + unmanage(_executor); // inherited from server + addBean(_scheduler); + addBean(_byteBufferPool); + + for (ConnectionFactory factory:factories) + addConnectionFactory(factory); + + int cores = Runtime.getRuntime().availableProcessors(); + if (acceptors < 0) + acceptors = 1 + cores / 16; + if (acceptors > 2 * cores) + LOG.warn("Acceptors should be <= 2*availableProcessors: " + this); + _acceptors = new Thread[acceptors]; + } + + + @Override + public Server getServer() + { + return _server; + } + + @Override + public Executor getExecutor() + { + return _executor; + } + + @Override + public ByteBufferPool getByteBufferPool() + { + return _byteBufferPool; + } + + @Override + @ManagedAttribute("Idle timeout") + public long getIdleTimeout() + { + return _idleTimeout; + } + + /** + *

    Sets the maximum Idle time for a connection, which roughly translates to the {@link Socket#setSoTimeout(int)} + * call, although with NIO implementations other mechanisms may be used to implement the timeout.

    + *

    The max idle time is applied:

    + *
      + *
    • When waiting for a new message to be received on a connection
    • + *
    • When waiting for a new message to be sent on a connection
    • + *
    + *

    This value is interpreted as the maximum time between some progress being made on the connection. + * So if a single byte is read or written, then the timeout is reset.

    + * + * @param idleTimeout the idle timeout + */ + public void setIdleTimeout(long idleTimeout) + { + _idleTimeout = idleTimeout; + } + + /** + * @return Returns the number of acceptor threads. + */ + @ManagedAttribute("number of acceptor threads") + public int getAcceptors() + { + return _acceptors.length; + } + + @Override + protected void doStart() throws Exception + { + _defaultConnectionFactory = getConnectionFactory(_defaultProtocol); + if(_defaultConnectionFactory==null) + throw new IllegalStateException("No protocol factory for default protocol: "+_defaultProtocol); + + super.doStart(); + + _stopping=new CountDownLatch(_acceptors.length); + for (int i = 0; i < _acceptors.length; i++) + getExecutor().execute(new Acceptor(i)); + + LOG.info("Started {}", this); + } + + + protected void interruptAcceptors() + { + synchronized (this) + { + for (Thread thread : _acceptors) + { + if (thread != null) + thread.interrupt(); + } + } + } + + @Override + public Future shutdown() + { + return new FutureCallback(true); + } + + @Override + protected void doStop() throws Exception + { + // Tell the acceptors we are stopping + interruptAcceptors(); + + // If we have a stop timeout + long stopTimeout = getStopTimeout(); + CountDownLatch stopping=_stopping; + if (stopTimeout > 0 && stopping!=null) + stopping.await(stopTimeout,TimeUnit.MILLISECONDS); + _stopping=null; + + super.doStop(); + + LOG.info("Stopped {}", this); + } + + public void join() throws InterruptedException + { + join(0); + } + + public void join(long timeout) throws InterruptedException + { + synchronized (this) + { + for (Thread thread : _acceptors) + if (thread != null) + thread.join(timeout); + } + } + + protected abstract void accept(int acceptorID) throws IOException, InterruptedException; + + + /* ------------------------------------------------------------ */ + /** + * @return Is the connector accepting new connections + */ + protected boolean isAccepting() + { + return isRunning(); + } + + @Override + public ConnectionFactory getConnectionFactory(String protocol) + { + synchronized (_factories) + { + return _factories.get(protocol.toLowerCase(Locale.ENGLISH)); + } + } + + @Override + public T getConnectionFactory(Class factoryType) + { + synchronized (_factories) + { + for (ConnectionFactory f : _factories.values()) + if (factoryType.isAssignableFrom(f.getClass())) + return (T)f; + return null; + } + } + + public void addConnectionFactory(ConnectionFactory factory) + { + synchronized (_factories) + { + ConnectionFactory old=_factories.remove(factory.getProtocol()); + if (old!=null) + removeBean(old); + _factories.put(factory.getProtocol().toLowerCase(Locale.ENGLISH), factory); + addBean(factory); + if (_defaultProtocol==null) + _defaultProtocol=factory.getProtocol(); + } + } + + public ConnectionFactory removeConnectionFactory(String protocol) + { + synchronized (_factories) + { + ConnectionFactory factory= _factories.remove(protocol.toLowerCase(Locale.ENGLISH)); + removeBean(factory); + return factory; + } + } + + @Override + public Collection getConnectionFactories() + { + synchronized (_factories) + { + return _factories.values(); + } + } + + public void setConnectionFactories(Collection factories) + { + synchronized (_factories) + { + List existing = new ArrayList<>(_factories.values()); + for (ConnectionFactory factory: existing) + removeConnectionFactory(factory.getProtocol()); + for (ConnectionFactory factory: factories) + if (factory!=null) + addConnectionFactory(factory); + } + } + + + @Override + @ManagedAttribute("Protocols supported by this connector") + public List getProtocols() + { + synchronized (_factories) + { + return new ArrayList<>(_factories.keySet()); + } + } + + public void clearConnectionFactories() + { + synchronized (_factories) + { + _factories.clear(); + } + } + + @ManagedAttribute("This connector's default protocol") + public String getDefaultProtocol() + { + return _defaultProtocol; + } + + public void setDefaultProtocol(String defaultProtocol) + { + _defaultProtocol = defaultProtocol.toLowerCase(Locale.ENGLISH); + if (isRunning()) + _defaultConnectionFactory=getConnectionFactory(_defaultProtocol); + } + + @Override + public ConnectionFactory getDefaultConnectionFactory() + { + if (isStarted()) + return _defaultConnectionFactory; + return getConnectionFactory(_defaultProtocol); + } + + private class Acceptor implements Runnable + { + private final int _acceptor; + + private Acceptor(int id) + { + _acceptor = id; + } + + @Override + public void run() + { + Thread current = Thread.currentThread(); + String name = current.getName(); + current.setName(name + "-acceptor-" + _acceptor + "-" + AbstractConnector.this); + + synchronized (AbstractConnector.this) + { + _acceptors[_acceptor] = current; + } + + try + { + while (isAccepting()) + { + try + { + accept(_acceptor); + } + catch (Throwable e) + { + if (isAccepting()) + LOG.warn(e); + else + LOG.ignore(e); + } + } + } + finally + { + current.setName(name); + + synchronized (AbstractConnector.this) + { + _acceptors[_acceptor] = null; + } + CountDownLatch stopping=_stopping; + if (stopping!=null) + stopping.countDown(); + } + } + } + + + + +// protected void connectionOpened(Connection connection) +// { +// _stats.connectionOpened(); +// connection.onOpen(); +// } +// +// protected void connectionClosed(Connection connection) +// { +// connection.onClose(); +// long duration = System.currentTimeMillis() - connection.getEndPoint().getCreatedTimeStamp(); +// _stats.connectionClosed(duration, connection.getMessagesIn(), connection.getMessagesOut()); +// } +// +// public void connectionUpgraded(Connection oldConnection, Connection newConnection) +// { +// oldConnection.onClose(); +// _stats.connectionUpgraded(oldConnection.getMessagesIn(), oldConnection.getMessagesOut()); +// newConnection.onOpen(); +// } + + @Override + public Collection getConnectedEndPoints() + { + return _immutableEndPoints; + } + + protected void onEndPointOpened(EndPoint endp) + { + _endpoints.add(endp); + } + + protected void onEndPointClosed(EndPoint endp) + { + _endpoints.remove(endp); + } + + @Override + public Scheduler getScheduler() + { + return _scheduler; + } + + @Override + public String getName() + { + return _name; + } + + /* ------------------------------------------------------------ */ + /** + * Set a connector name. A context may be configured with + * virtual hosts in the form "@contextname" and will only serve + * requests from the named connector, + * @param name A connector name. + */ + public void setName(String name) + { + _name=name; + } + + @Override + public String toString() + { + return String.format("%s@%x{%s}", + _name==null?getClass().getSimpleName():_name, + hashCode(), + getDefaultProtocol()); + } +}