From fcf635c6a4436df96ac25e50f76d7e2c78e971b9 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Mon, 03 Sep 2007 19:53:23 +0000
Subject: [PATCH] Implement support for a maximum blocked write time limit in the LDAP connection handler, which can be used to terminate client connections if an attempt to write data to the client has been blocked for too long. This will generally occur if the client has become unresponsive or there is a network outage.
---
opends/resource/schema/02-config.ldif | 7
opends/src/server/org/opends/server/api/ClientConnection.java | 51 ++++
opends/src/admin/defn/org/opends/server/admin/std/LDAPConnectionHandlerConfiguration.xml | 25 ++
opends/src/server/org/opends/server/protocols/ldap/LDAPConnectionHandler.java | 14 +
opends/src/server/org/opends/server/extensions/NullConnectionSecurityProvider.java | 266 +++++++++++++++--------
opends/src/server/org/opends/server/extensions/TLSConnectionSecurityProvider.java | 167 +++++----------
opends/resource/config/config.ldif | 2
opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java | 84 ++++++-
8 files changed, 392 insertions(+), 224 deletions(-)
diff --git a/opends/resource/config/config.ldif b/opends/resource/config/config.ldif
index e042b40..b992ba3 100644
--- a/opends/resource/config/config.ldif
+++ b/opends/resource/config/config.ldif
@@ -425,6 +425,7 @@
ds-cfg-allow-tcp-reuse-address: true
ds-cfg-send-rejection-notice: true
ds-cfg-max-request-size: 5 megabytes
+ds-cfg-max-blocked-write-time-limit: 2 minutes
ds-cfg-num-request-handlers: 2
ds-cfg-allow-start-tls: false
ds-cfg-use-ssl: false
@@ -448,6 +449,7 @@
ds-cfg-allow-tcp-reuse-address: true
ds-cfg-send-rejection-notice: true
ds-cfg-max-request-size: 5 megabytes
+ds-cfg-max-blocked-write-time-limit: 2 minutes
ds-cfg-num-request-handlers: 2
ds-cfg-allow-start-tls: false
ds-cfg-use-ssl: true
diff --git a/opends/resource/schema/02-config.ldif b/opends/resource/schema/02-config.ldif
index a4aa90b..4148466 100644
--- a/opends/resource/schema/02-config.ldif
+++ b/opends/resource/schema/02-config.ldif
@@ -1642,6 +1642,10 @@
NAME 'ds-cfg-referential-integrity-base-dn'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.486
+ NAME 'ds-cfg-max-blocked-write-time-limit'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE
+ X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
NAME 'ds-cfg-access-control-handler' SUP top STRUCTURAL
MUST ( cn $ ds-cfg-acl-handler-class $ ds-cfg-acl-handler-enabled )
@@ -1735,7 +1739,8 @@
ds-cfg-ssl-client-auth-policy $ ds-cfg-ssl-cert-nickname $
ds-cfg-accept-backlog $ ds-cfg-key-manager-provider-dn $
ds-cfg-trust-manager-provider-dn $ ds-cfg-ssl-protocol $
- ds-cfg-ssl-cipher-suite ) X-ORIGIN 'OpenDS Directory Server' )
+ ds-cfg-ssl-cipher-suite $ ds-cfg-max-blocked-write-time-limit )
+ X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.15 NAME 'ds-cfg-entry-cache'
SUP top STRUCTURAL MUST ( cn $ ds-cfg-entry-cache-class $
ds-cfg-entry-cache-enabled ) X-ORIGIN 'OpenDS Directory Server' )
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/LDAPConnectionHandlerConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/LDAPConnectionHandlerConfiguration.xml
index bfb781a..2e877fb 100644
--- a/opends/src/admin/defn/org/opends/server/admin/std/LDAPConnectionHandlerConfiguration.xml
+++ b/opends/src/admin/defn/org/opends/server/admin/std/LDAPConnectionHandlerConfiguration.xml
@@ -521,5 +521,30 @@
</adm:profile>
</adm:property>
+ <adm:property name="max-blocked-write-time-limit" mandatory="false">
+ <adm:synopsis>
+ Specifies the maximum length of time that attempts to write data to LDAP
+ clients should be allowed to block. If an attempt to write data to a
+ client takes longer than this length of time, then the client connection
+ will be terminated.
+ </adm:synopsis>
+ <adm:default-behavior>
+ <adm:defined>
+ <adm:value>
+ 2 minutes
+ </adm:value>
+ </adm:defined>
+ </adm:default-behavior>
+ <adm:syntax>
+ <adm:duration base-unit="ms" lower-limit="0" />
+ </adm:syntax>
+ <adm:profile name="ldap">
+ <ldap:attribute>
+ <ldap:oid>1.3.6.1.4.1.26027.1.1.486</ldap:oid>
+ <ldap:name>ds-cfg-max-blocked-write-time-limit</ldap:name>
+ </ldap:attribute>
+ </adm:profile>
+ </adm:property>
+
</adm:managed-object>
diff --git a/opends/src/server/org/opends/server/api/ClientConnection.java b/opends/src/server/org/opends/server/api/ClientConnection.java
index a001892..64500af 100644
--- a/opends/src/server/org/opends/server/api/ClientConnection.java
+++ b/opends/src/server/org/opends/server/api/ClientConnection.java
@@ -25,24 +25,26 @@
* Portions Copyright 2006-2007 Sun Microsystems, Inc.
*/
package org.opends.server.api;
-import org.opends.messages.Message;
import java.net.InetAddress;
import java.nio.ByteBuffer;
+import java.nio.channels.Selector;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
+import org.opends.messages.Message;
import org.opends.server.api.plugin.IntermediateResponsePluginResult;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.PersistentSearch;
import org.opends.server.core.PluginConfigManager;
import org.opends.server.core.SearchOperation;
import org.opends.server.core.NetworkGroup;
+import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.AbstractOperation;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
@@ -62,11 +64,9 @@
import org.opends.server.types.SearchResultReference;
import org.opends.server.util.TimeThread;
+import static org.opends.messages.CoreMessages.*;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
-import org.opends.server.loggers.debug.DebugTracer;
-import static org.opends.messages.CoreMessages.*;
-
import static org.opends.server.util.StaticUtils.*;
@@ -402,6 +402,49 @@
/**
+ * Retrieves a {@code Selector} that may be used to ensure that
+ * write operations complete in a timely manner, or terminate the
+ * connection in the event that they fail to do so. This is an
+ * optional method for client connections, and the default
+ * implementation returns {@code null} to indicate that the maximum
+ * blocked write time limit is not supported for this connection.
+ * Subclasses that do wish to support this functionality should
+ * return a valid {@code Selector} object.
+ *
+ * @return The {@code Selector} that may be used to ensure that
+ * write operations complete in a timely manner, or
+ * {@code null} if this client connection does not support
+ * maximum blocked write time limit functionality.
+ */
+ public Selector getWriteSelector()
+ {
+ // There will not be a write selector in the default
+ // implementation.
+ return null;
+ }
+
+
+
+ /**
+ * Retrieves the maximum length of time in milliseconds that
+ * attempts to write data to the client should be allowed to block.
+ * A value of zero indicates there should be no limit.
+ *
+ * @return The maximum length of time in milliseconds that attempts
+ * to write data to the client should be allowed to block,
+ * or zero if there should be no limit.
+ */
+ public long getMaxBlockedWriteTimeLimit()
+ {
+ // By default, we'll return 0, which indicates that there should
+ // be no maximum time limit. Subclasses should override this if
+ // they want to support a maximum blocked write time limit.
+ return 0L;
+ }
+
+
+
+ /**
* Indicates that the data in the provided buffer has been read from
* the client and should be processed. The contents of the provided
* buffer will be in clear-text (the data may have been passed
diff --git a/opends/src/server/org/opends/server/extensions/NullConnectionSecurityProvider.java b/opends/src/server/org/opends/server/extensions/NullConnectionSecurityProvider.java
index ef52f6c..8800955 100644
--- a/opends/src/server/org/opends/server/extensions/NullConnectionSecurityProvider.java
+++ b/opends/src/server/org/opends/server/extensions/NullConnectionSecurityProvider.java
@@ -30,23 +30,27 @@
import java.io.IOException;
import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
+import java.util.Iterator;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.ConnectionSecurityProvider;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
+import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DisconnectReason;
import org.opends.server.types.InitializationException;
import org.opends.server.types.DebugLogLevel;
-import static org.opends.server.loggers.debug.DebugLogger.*;
-import org.opends.server.loggers.debug.DebugTracer;
import static org.opends.messages.ExtensionMessages.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.util.StaticUtils.*;
+
/**
* This class provides an implementation of a connection security provider that
* does not actually provide any security for the communication process. Any
@@ -62,7 +66,6 @@
-
/**
* The buffer size in bytes that will be used for data on this connection.
*/
@@ -92,7 +95,6 @@
public NullConnectionSecurityProvider()
{
super();
-
}
@@ -121,20 +123,9 @@
/**
- * Initializes this connection security provider using the information in the
- * provided configuration entry.
- *
- * @param configEntry The entry that contains the configuration for this
- * connection security provider.
- *
- * @throws ConfigException If the provided entry does not contain an
- * acceptable configuration for this security
- * provider.
- *
- * @throws InitializationException If a problem occurs during initialization
- * that is not related to the provided
- * configuration.
+ * {@inheritDoc}
*/
+ @Override()
public void initializeConnectionSecurityProvider(ConfigEntry configEntry)
throws ConfigException, InitializationException
{
@@ -146,9 +137,9 @@
/**
- * Performs any finalization that may be necessary for this connection
- * security provider.
+ * {@inheritDoc}
*/
+ @Override()
public void finalizeConnectionSecurityProvider()
{
// No implementation is required.
@@ -157,10 +148,9 @@
/**
- * Retrieves the name used to identify this security mechanism.
- *
- * @return The name used to identify this security mechanism.
+ * {@inheritDoc}
*/
+ @Override()
public String getSecurityMechanismName()
{
return "NULL";
@@ -169,13 +159,9 @@
/**
- * Indicates whether client connections using this connection security
- * provider should be considered secure.
- *
- * @return <CODE>true</CODE> if client connections using this connection
- * security provider should be considered secure, or
- * <CODE>false</CODE> if not.
+ * {@inheritDoc}
*/
+ @Override()
public boolean isSecure()
{
// This is not a secure provider.
@@ -185,21 +171,9 @@
/**
- * Creates a new instance of this connection security provider that will be
- * used to encode and decode all communication on the provided client
- * connection.
- *
- * @param clientConnection The client connection with which this security
- * provider will be associated.
- * @param socketChannel The socket channel that may be used to
- * communicate with the client.
- *
- * @return The created connection security provider instance.
- *
- * @throws DirectoryException If a problem occurs while creating a new
- * instance of this security provider for the
- * given client connection.
+ * {@inheritDoc}
*/
+ @Override()
public ConnectionSecurityProvider newInstance(ClientConnection
clientConnection,
SocketChannel socketChannel)
@@ -212,20 +186,9 @@
/**
- * Indicates that the associated client connection is being closed and that
- * this security provider should perform any necessary processing to deal with
- * that. If it is indicated that the connection is still valid, then the
- * security provider may attempt to communicate with the client to perform a
- * graceful shutdown.
- *
- * @param connectionValid Indicates whether the Directory Server believes
- * that the client connection is still valid and may
- * be used for communication with the client. Note
- * that this may be inaccurate, or that the state of
- * the connection may change during the course of
- * this method, so the security provider must be able
- * to handle failures if they arise.
+ * {@inheritDoc}
*/
+ @Override()
public void disconnect(boolean connectionValid)
{
// No implementation is required.
@@ -234,13 +197,9 @@
/**
- * Retrieves the size in bytes that the client should use for the byte buffer
- * meant to hold clear-text data read from or to be written to the client.
- *
- * @return The size in bytes that the client should use for the byte buffer
- * meant to hold clear-text data read from or to be written to the
- * client.
+ * {@inheritDoc}
*/
+ @Override()
public int getClearBufferSize()
{
return BUFFER_SIZE;
@@ -249,13 +208,9 @@
/**
- * Retrieves the size in bytes that the client should use for the byte buffer
- * meant to hold encoded data read from or to be written to the client.
- *
- * @return The size in bytes that the client should use for the byte buffer
- * meant to hold encoded data read from or to be written to the
- * client.
+ * {@inheritDoc}
*/
+ @Override()
public int getEncodedBufferSize()
{
return BUFFER_SIZE;
@@ -264,18 +219,9 @@
/**
- * Reads data from a client connection, performing any necessary negotiation
- * in the process. Whenever any clear-text data has been obtained, then the
- * connection security provider should make that available to the client by
- * calling the <CODE>ClientConnection.processDataRead</CODE> method.
- *
- * @return <CODE>true</CODE> if all the data in the provided buffer was
- * processed and the client connection can remain established, or
- * <CODE>false</CODE> if a decoding error occurred and requests from
- * this client should no longer be processed. Note that if this
- * method does return <CODE>false</CODE>, then it must have already
- * disconnected the client.
+ * {@inheritDoc}
*/
+ @Override()
public boolean readData()
{
clearBuffer.clear();
@@ -342,23 +288,9 @@
/**
- * Writes the data contained in the provided clear-text buffer to the client,
- * performing any necessary encoding in the process. It must be capable of
- * dealing with input buffers that are larger than the value returned by the
- * <CODE>getClearBufferSize</CODE> method. When this method returns, the
- * provided buffer should be in its original state with regard to the position
- * and limit.
- *
- * @param clearData The buffer containing the clear-text data to write to
- * the client.
- *
- * @return <CODE>true</CODE> if all the data in the provided buffer was
- * written to the client and the connection may remain established,
- * or <CODE>false</CODE> if a problem occurred and the client
- * connection is no longer valid. Note that if this method does
- * return <CODE>false</CODE>, then it must have already disconnected
- * the client.
+ * {@inheritDoc}
*/
+ @Override()
public boolean writeData(ByteBuffer clearData)
{
int position = clearData.position();
@@ -376,6 +308,14 @@
null);
return false;
}
+ else if (bytesWritten == 0)
+ {
+ // This can happen if the server can't send data to the client (e.g.,
+ // because the client is blocked or there is a network problem. In
+ // that case, then use a selector to perform the write, timing out and
+ // terminating the client connection if necessary.
+ return writeWithTimeout(clientConnection, socketChannel, clearData);
+ }
}
return true;
@@ -411,5 +351,143 @@
clearData.limit(limit);
}
}
+
+
+
+ /**
+ * Writes the contents of the provided buffer to the client, terminating the
+ * connection if the write is unsuccessful for too long (e.g., if the client
+ * is unresponsive or there is a network problem). If possible, it will
+ * attempt to use the selector returned by the
+ * {@code ClientConnection.getWriteSelector} method, but it is capable of
+ * working even if that method returns {@code null}.
+ * <BR><BR>
+ * Note that this method has been written in a generic manner so that other
+ * connection security providers can use it to send data to the client,
+ * provided that the given buffer contains the appropriate pre-encoded
+ * information.
+ * <BR><BR>
+ * Also note that the original position and limit values will not be
+ * preserved, so if that is important to the caller, then it should record
+ * them before calling this method and restore them after it returns.
+ *
+ * @param clientConnection The client connection to which the data is to be
+ * written.
+ * @param socketChannel The socket channel over which to write the data.
+ * @param buffer The data to be written to the client.
+ *
+ * @return <CODE>true</CODE> if all the data in the provided buffer was
+ * written to the client and the connection may remain established,
+ * or <CODE>false</CODE> if a problem occurred and the client
+ * connection is no longer valid. Note that if this method does
+ * return <CODE>false</CODE>, then it must have already disconnected
+ * the client.
+ *
+ * @throws IOException If a problem occurs while attempting to write data
+ * to the client. The caller will be responsible for
+ * catching this and terminating the client connection.
+ */
+ public static boolean writeWithTimeout(ClientConnection clientConnection,
+ SocketChannel socketChannel,
+ ByteBuffer buffer)
+ throws IOException
+ {
+ long startTime = System.currentTimeMillis();
+ long waitTime = clientConnection.getMaxBlockedWriteTimeLimit();
+ if (waitTime <= 0)
+ {
+ // We won't support an infinite time limit, so fall back to using
+ // five minutes, which is a very long timeout given that we're
+ // blocking a worker thread.
+ waitTime = 300000L;
+ }
+
+ long stopTime = startTime + waitTime;
+
+
+ Selector selector = clientConnection.getWriteSelector();
+ if (selector == null)
+ {
+ // The client connection does not provide a selector, so we'll fall back
+ // to a more inefficient way that will work without a selector.
+ while (buffer.hasRemaining() && (System.currentTimeMillis() < stopTime))
+ {
+ if (socketChannel.write(buffer) < 0)
+ {
+ // The client connection has been closed. Disconnect and return.
+ clientConnection.disconnect(DisconnectReason.CLIENT_DISCONNECT, false,
+ null);
+ return false;
+ }
+ }
+
+ if (buffer.hasRemaining())
+ {
+ // If we've gotten here, then the write timed out. Terminate the client
+ // connection.
+ clientConnection.disconnect(DisconnectReason.IO_TIMEOUT, false, null);
+ return false;
+ }
+
+ return true;
+ }
+
+
+ // Register with the selector for handling write operations.
+ SelectionKey key = socketChannel.register(selector, SelectionKey.OP_WRITE);
+
+ try
+ {
+ selector.select(waitTime);
+ while (buffer.hasRemaining())
+ {
+ long currentTime = System.currentTimeMillis();
+ if (currentTime >= stopTime)
+ {
+ // We've been blocked for too long. Terminate the client connection.
+ clientConnection.disconnect(DisconnectReason.IO_TIMEOUT, false, null);
+ return false;
+ }
+ else
+ {
+ waitTime = stopTime - currentTime;
+ }
+
+ Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
+ while (iterator.hasNext())
+ {
+ SelectionKey k = iterator.next();
+ if (k.isWritable())
+ {
+ int bytesWritten = socketChannel.write(buffer);
+ if (bytesWritten < 0)
+ {
+ // The client connection has been closed. Disconnect and return.
+ clientConnection.disconnect(DisconnectReason.CLIENT_DISCONNECT,
+ false, null);
+ return false;
+ }
+
+ iterator.remove();
+ }
+ }
+
+ if (buffer.hasRemaining())
+ {
+ selector.select(waitTime);
+ }
+ }
+
+ return true;
+ }
+ finally
+ {
+ if (key.isValid())
+ {
+ key.cancel();
+ selector.selectNow();
+ }
+ }
+ }
}
diff --git a/opends/src/server/org/opends/server/extensions/TLSConnectionSecurityProvider.java b/opends/src/server/org/opends/server/extensions/TLSConnectionSecurityProvider.java
index 0912664..b84d4f7 100644
--- a/opends/src/server/org/opends/server/extensions/TLSConnectionSecurityProvider.java
+++ b/opends/src/server/org/opends/server/extensions/TLSConnectionSecurityProvider.java
@@ -25,7 +25,6 @@
* Portions Copyright 2006-2007 Sun Microsystems, Inc.
*/
package org.opends.server.extensions;
-import org.opends.messages.Message;
@@ -40,6 +39,7 @@
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLSession;
+import org.opends.messages.Message;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.ConnectionSecurityProvider;
import org.opends.server.api.KeyManagerProvider;
@@ -47,16 +47,16 @@
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DisconnectReason;
import org.opends.server.types.InitializationException;
import org.opends.server.types.SSLClientAuthPolicy;
import org.opends.server.util.SelectableCertificateKeyManager;
-import static org.opends.server.loggers.debug.DebugLogger.*;
-import org.opends.server.loggers.debug.DebugTracer;
-import org.opends.server.types.DebugLogLevel;
import static org.opends.messages.ExtensionMessages.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
import static org.opends.server.util.StaticUtils.*;
@@ -64,7 +64,7 @@
/**
* This class provides an implementation of a connection security provider that
* uses SSL/TLS to encrypt the communication to and from the client. It uses
- * the <CODE>javax.net.ssl.SSLEngine</CODE> class to provide the actual SSL
+ * the {@code javax.net.ssl.SSLEngine} class to provide the actual SSL
* communication layer, and the Directory Server key and trust store providers
* to determine which key and trust stores to use.
*/
@@ -132,10 +132,10 @@
/**
* Creates a new instance of this connection security provider. Note that
* no initialization should be done here, since it should all be done in the
- * <CODE>initializeConnectionSecurityProvider</CODE> method. Also note that
- * this instance should only be used to create new instances that are
- * associated with specific client connections. This instance itself should
- * not be used to attempt secure communication with the client.
+ * {@code initializeConnectionSecurityProvider} method. Also note that this
+ * instance should only be used to create new instances that are associated
+ * with specific client connections. This instance itself should not be used
+ * to attempt secure communication with the client.
*/
public TLSConnectionSecurityProvider()
{
@@ -276,20 +276,9 @@
/**
- * Initializes this connection security provider using the information in the
- * provided configuration entry.
- *
- * @param configEntry The entry that contains the configuration for this
- * connection security provider.
- *
- * @throws ConfigException If the provided entry does not contain an
- * acceptable configuration for this security
- * provider.
- *
- * @throws InitializationException If a problem occurs during initialization
- * that is not related to the provided
- * configuration.
+ * {@inheritDoc}
*/
+ @Override()
public void initializeConnectionSecurityProvider(ConfigEntry configEntry)
throws ConfigException, InitializationException
{
@@ -314,9 +303,9 @@
/**
- * Performs any finalization that may be necessary for this connection
- * security provider.
+ * {@inheritDoc}
*/
+ @Override()
public void finalizeConnectionSecurityProvider()
{
// No implementation is required.
@@ -325,10 +314,9 @@
/**
- * Retrieves the name used to identify this security mechanism.
- *
- * @return The name used to identify this security mechanism.
+ * {@inheritDoc}
*/
+ @Override()
public String getSecurityMechanismName()
{
return SSL_CONTEXT_INSTANCE_NAME;
@@ -337,13 +325,9 @@
/**
- * Indicates whether client connections using this connection security
- * provider should be considered secure.
- *
- * @return <CODE>true</CODE> if client connections using this connection
- * security provider should be considered secure, or
- * <CODE>false</CODE> if not.
+ * {@inheritDoc}
*/
+ @Override()
public boolean isSecure()
{
// This should be considered secure.
@@ -353,21 +337,9 @@
/**
- * Creates a new instance of this connection security provider that will be
- * used to encode and decode all communication on the provided client
- * connection.
- *
- * @param clientConnection The client connection with which this security
- * provider will be associated.
- * @param socketChannel The socket channel that may be used to
- * communicate with the client.
- *
- * @return The created connection security provider instance.
- *
- * @throws DirectoryException If a problem occurs while creating a new
- * instance of this security provider for the
- * given client connection.
+ * {@inheritDoc}
*/
+ @Override()
public ConnectionSecurityProvider newInstance(ClientConnection
clientConnection,
SocketChannel socketChannel)
@@ -380,20 +352,9 @@
/**
- * Indicates that the associated client connection is being closed and that
- * this security provider should perform any necessary processing to deal with
- * that. If it is indicated that the connection is still valid, then the
- * security provider may attempt to communicate with the client to perform a
- * graceful shutdown.
- *
- * @param connectionValid Indicates whether the Directory Server believes
- * that the client connection is still valid and may
- * be used for communication with the client. Note
- * that this may be inaccurate, or that the state of
- * the connection may change during the course of
- * this method, so the security provider must be able
- * to handle failures if they arise.
+ * {@inheritDoc}
*/
+ @Override()
public void disconnect(boolean connectionValid)
{
if (connectionValid)
@@ -468,13 +429,9 @@
/**
- * Retrieves the size in bytes that the client should use for the byte buffer
- * meant to hold clear-text data read from or to be written to the client.
- *
- * @return The size in bytes that the client should use for the byte buffer
- * meant to hold clear-text data read from or to be written to the
- * client.
+ * {@inheritDoc}
*/
+ @Override()
public int getClearBufferSize()
{
return clearBufferSize;
@@ -483,13 +440,9 @@
/**
- * Retrieves the size in bytes that the client should use for the byte buffer
- * meant to hold encoded data read from or to be written to the client.
- *
- * @return The size in bytes that the client should use for the byte buffer
- * meant to hold encoded data read from or to be written to the
- * client.
+ * {@inheritDoc}
*/
+ @Override()
public int getEncodedBufferSize()
{
return sslBufferSize;
@@ -498,18 +451,9 @@
/**
- * Reads data from a client connection, performing any necessary negotiation
- * in the process. Whenever any clear-text data has been obtained, then the
- * connection security provider should make that available to the client by
- * calling the <CODE>ClientConnection.processDataRead</CODE> method.
- *
- * @return <CODE>true</CODE> if all the data in the provided buffer was
- * processed and the client connection can remain established, or
- * <CODE>false</CODE> if a decoding error occurred and requests from
- * this client should no longer be processed. Note that if this
- * method does return <CODE>false</CODE>, then it must have already
- * disconnected the client.
+ * {@inheritDoc}
*/
+ @Override()
public boolean readData()
{
while (true)
@@ -675,23 +619,9 @@
/**
- * Writes the data contained in the provided clear-text buffer to the client,
- * performing any necessary encoding in the process. It must be capable of
- * dealing with input buffers that are larger than the value returned by the
- * <CODE>getClearBufferSize</CODE> method. When this method returns, the
- * provided buffer should be in its original state with regard to the position
- * and limit.
- *
- * @param clearData The buffer containing the clear-text data to write to
- * the client.
- *
- * @return <CODE>true</CODE> if all the data in the provided buffer was
- * written to the client and the connection may remain established,
- * or <CODE>false</CODE> if a problem occurred and the client
- * connection is no longer valid. Note that if this method does
- * return <CODE>false</CODE>, then it must have already disconnected
- * the client.
+ * {@inheritDoc}
*/
+ @Override()
public boolean writeData(ByteBuffer clearData)
{
int originalPosition = clearData.position();
@@ -741,17 +671,16 @@
* Writes the data contained in the provided clear-text buffer to the client,
* performing any necessary encoding in the process. The amount of data in
* the provided buffer must be less than or equal to the value returned by the
- * <CODE>getClearBufferSize</CODE> method.
+ * {@code getClearBufferSize} method.
*
* @param clearData The buffer containing the clear-text data to write to
* the client.
*
- * @return <CODE>true</CODE> if all the data in the provided buffer was
- * written to the client and the connection may remain established,
- * or <CODE>false</CODE> if a problem occurred and the client
- * connection is no longer valid. Note that if this method does
- * return <CODE>false</CODE>, then it must have already disconnected
- * the client.
+ * @return {@code true} if all the data in the provided buffer was written to
+ * the client and the connection may remain established, or
+ * {@code false} if a problem occurred and the client connection is
+ * no longer valid. Note that if this method does return
+ * {@code false}, then it must have already disconnected the client.
*/
private boolean writeInternal(ByteBuffer clearData)
{
@@ -785,6 +714,11 @@
DisconnectReason.CLIENT_DISCONNECT, false, null);
return false;
}
+ else if (bytesWritten == 0)
+ {
+ return NullConnectionSecurityProvider.writeWithTimeout(
+ clientConnection, socketChannel, sslOutBuffer);
+ }
}
break;
@@ -885,6 +819,11 @@
DisconnectReason.CLIENT_DISCONNECT, false, null);
return false;
}
+ else if (bytesWritten == 0)
+ {
+ return NullConnectionSecurityProvider.writeWithTimeout(
+ clientConnection, socketChannel, sslOutBuffer);
+ }
}
break;
@@ -935,6 +874,11 @@
false, null);
return false;
}
+ else if (bytesWritten == 0)
+ {
+ return NullConnectionSecurityProvider.writeWithTimeout(
+ clientConnection, socketChannel, sslOutBuffer);
+ }
}
}
@@ -976,8 +920,8 @@
/**
* Retrieves the set of SSL protocols that will be allowed.
*
- * @return The set of SSL protocols that will be allowed, or
- * <CODE>null</CODE> if the default set will be used.
+ * @return The set of SSL protocols that will be allowed, or {@code null} if
+ * the default set will be used.
*/
public String[] getEnabledProtocols()
{
@@ -990,8 +934,7 @@
* Specifies the set of SSL protocols that will be allowed.
*
* @param enabledProtocols The set of SSL protocols that will be allowed, or
- * <CODE>null</CODE> if the default set will be
- * used.
+ * {@code null} if the default set will be used.
*/
public void setEnabledProtocols(String[] enabledProtocols)
{
@@ -1068,8 +1011,8 @@
* listed, followed by the certificates of any issuers in the chain.
*
* @return The certificate chain that the client presented to the server
- * during the handshake process, or <CODE>null</CODE> if the client
- * did not present a certificate.
+ * during the handshake process, or {@code null} if the client did
+ * not present a certificate.
*/
public Certificate[] getClientCertificateChain()
{
diff --git a/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java b/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
index 34b157d..28037e4 100644
--- a/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
+++ b/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
@@ -25,22 +25,12 @@
* Portions Copyright 2006-2007 Sun Microsystems, Inc.
*/
package org.opends.server.protocols.ldap;
-import org.opends.messages.Message;
-import static org.opends.server.loggers.AccessLogger.logDisconnect;
-import static org.opends.server.loggers.ErrorLogger.logError;
-import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
-import static org.opends.server.loggers.debug.DebugLogger.getTracer;
-import static org.opends.messages.ProtocolMessages.*;
-import org.opends.messages.MessageBuilder;
-import static org.opends.server.protocols.ldap.LDAPConstants.*;
-import static org.opends.server.util.StaticUtils.getExceptionMessage;
-import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
-
import java.net.InetAddress;
import java.nio.ByteBuffer;
+import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Collection;
@@ -48,8 +38,11 @@
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
+import org.opends.messages.Message;
+import org.opends.messages.MessageBuilder;
import org.opends.server.api.ClientConnection;
import org.opends.server.api.ConnectionHandler;
import org.opends.server.api.ConnectionSecurityProvider;
@@ -82,8 +75,6 @@
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DisconnectReason;
-
-
import org.opends.server.types.IntermediateResponse;
import org.opends.server.types.Operation;
import org.opends.server.types.ResultCode;
@@ -91,6 +82,15 @@
import org.opends.server.types.SearchResultReference;
import org.opends.server.util.TimeThread;
+import static org.opends.messages.ProtocolMessages.*;
+import static org.opends.server.loggers.AccessLogger.logDisconnect;
+import static org.opends.server.loggers.ErrorLogger.logError;
+import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
+import static org.opends.server.loggers.debug.DebugLogger.getTracer;
+import static org.opends.server.protocols.ldap.LDAPConstants.*;
+import static org.opends.server.util.StaticUtils.getExceptionMessage;
+import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
+
/**
@@ -115,6 +115,9 @@
// The next operation ID that should be used for this connection.
private AtomicLong nextOperationID;
+ // The selector that may be used for write operations.
+ private AtomicReference<Selector> writeSelector;
+
// Indicates whether the Directory Server believes this connection to be
// valid and available for communication.
private boolean connectionValid;
@@ -251,6 +254,7 @@
operationsInProgress = new ConcurrentHashMap<Integer,AbstractOperation>();
keepStats = connectionHandler.keepStats();
protocol = "LDAP";
+ writeSelector = new AtomicReference<Selector>();
clientAddress = clientChannel.socket().getInetAddress().getHostAddress();
clientPort = clientChannel.socket().getPort();
@@ -992,6 +996,17 @@
finalizeConnectionInternal();
+ // If there is a write selector for this connection, then close it.
+ Selector selector = writeSelector.get();
+ if (selector != null)
+ {
+ try
+ {
+ selector.close();
+ } catch (Exception e) {}
+ }
+
+
// See if we should send a notification to the client. If so, then
// construct and send a notice of disconnection unsolicited response.
// Note that we cannot send this notification to an LDAPv2 client.
@@ -1430,6 +1445,49 @@
/**
+ * {@inheritDoc}
+ */
+ @Override()
+ public Selector getWriteSelector()
+ {
+ Selector selector = writeSelector.get();
+ if (selector == null)
+ {
+ try
+ {
+ selector = Selector.open();
+ if (! writeSelector.compareAndSet(null, selector))
+ {
+ selector.close();
+ selector = writeSelector.get();
+ }
+ }
+ catch (Exception e)
+ {
+ if (debugEnabled())
+ {
+ TRACER.debugCaught(DebugLogLevel.ERROR, e);
+ }
+ }
+ }
+
+ return selector;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override()
+ public long getMaxBlockedWriteTimeLimit()
+ {
+ return connectionHandler.getMaxBlockedWriteTimeLimit();
+ }
+
+
+
+ /**
* Process the information contained in the provided byte buffer as an ASN.1
* element. It may take several calls to this method in order to get all the
* information necessary to decode a single ASN.1 element, but it may also be
diff --git a/opends/src/server/org/opends/server/protocols/ldap/LDAPConnectionHandler.java b/opends/src/server/org/opends/server/protocols/ldap/LDAPConnectionHandler.java
index f44cba2..a853cff 100644
--- a/opends/src/server/org/opends/server/protocols/ldap/LDAPConnectionHandler.java
+++ b/opends/src/server/org/opends/server/protocols/ldap/LDAPConnectionHandler.java
@@ -486,6 +486,20 @@
/**
+ * Retrieves the maximum length of time in milliseconds that attempts to write
+ * to LDAP client connections should be allowed to block.
+ *
+ * @return The maximum length of time in milliseconds that attempts to write
+ * to LDAP client connections should be allowed to block, or zero if
+ * there should not be any limit imposed.
+ */
+ public long getMaxBlockedWriteTimeLimit() {
+ return currentConfig.getMaxBlockedWriteTimeLimit();
+ }
+
+
+
+ /**
* Retrieves the maximum ASN.1 element value length that will be
* allowed by this connection handler.
*
--
Gitblit v1.10.0