You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
LDAPConnectionHandler2 ignores use-tcp-keep-alive, use-tcp-no-delay, buffer-size and num-request-handlers, and applies allow-tcp-reuse-address only to its pre-check #1119
org.forgerock.opendj.reactive.LDAPConnectionHandler2, the class every shipped LDAP listener runs on (opendj-server-legacy/resource/config/config.ldif, lines 316 and 340), never reads four properties of its own configuration: use-tcp-keep-alive, use-tcp-no-delay, buffer-size and num-request-handlers. A fifth one, allow-tcp-reuse-address, is only half applied. dsconfig accepts any value for them, the configuration entry stores it, and the handler keeps the same behaviour. Nothing in the configuration reference or in the server output tells the administrator that the setting has no effect.
The legacy org.opends.server.protocols.ldap.LDAPConnectionHandler does apply all five, so the properties only work on the handler that no fresh install uses.
Cause
LDAPConnectionHandler2.startListener() builds the LDAPListener with only two options taken from the configuration (LDAPConnectionHandler2.java, lines 683-685):
None of isUseTCPKeepAlive(), isUseTCPNoDelay(), getBufferSize() or getNumRequestHandlers() is called anywhere in the org.forgerock.opendj.reactive package.
Property
Legacy handler
LDAPConnectionHandler2
use-tcp-keep-alive
socket().setKeepAlive(...) on every accepted channel (LDAPConnectionHandler.java, line 1069)
not read; GrizzlyUtils.configureConnection applies the SDK option SO_KEEPALIVE, default true (system property org.forgerock.opendj.io.keepAlive)
use-tcp-no-delay
socket().setTcpNoDelay(...) (line 1070)
not read; SDK option TCP_NO_DELAY, default true (system property org.forgerock.opendj.io.tcpNoDelay)
buffer-size
sizes the per-connection buffer (LDAPClientConnection.java, lines 478-482)
not read
num-request-handlers
number of request-handler threads (LDAPConnectionHandler.java, line 619)
not read; connections are served by the Grizzly selector threads of the shared ServerTCPNIOTransport, sized by the JVM-wide system property org.forgerock.opendj.transport.selectors
allow-tcp-reuse-address
setReuseAddress(...) on the listening channel (line 1044) and the "address in use" pre-check
only the pre-check (LDAPConnectionHandler2.java, lines 503, 527 and 611-614); the listening socket is bound by the shared transport, whose SO_REUSEADDR is enabled by default and can only be changed JVM-wide with org.forgerock.opendj.transport.reuseAddress (ServerTCPNIOTransport.java, lines 94-99)
Impact
use-tcp-keep-alive:false or use-tcp-no-delay:false has no effect on the default listeners: accepted sockets always get SO_KEEPALIVE and TCP_NODELAY switched on.
num-request-handlers is marked component-restart, yet restarting the handler changes nothing; the thread count can only be changed for the whole JVM.
buffer-size changes are silently dropped.
allow-tcp-reuse-address:false makes the pre-check stricter, but the listening socket is still bound with SO_REUSEADDR.
dsconfig set-connection-handler-prop --handler-name "LDAP Connection Handler" \
--set use-tcp-no-delay:false --set use-tcp-keep-alive:false \
-h localhost -p 4444 -D "cn=Directory Manager" -w password -X -n
# restart the handler (or the server), open an LDAP connection and inspect the accepted socket,
# e.g. with `ss -tnoi` on Linux: the keepalive timer is still present
Expected: the accepted socket has SO_KEEPALIVE and TCP_NODELAY switched off, as on a handler running the legacy class.
Proposal
use-tcp-keep-alive and use-tcp-no-delay: pass them to the listener as LDAPListener.SO_KEEPALIVE and LDAPListener.TCP_NO_DELAY. LDAPServerFilter already applies the listener options to every accepted connection through GrizzlyUtils.configureConnection, so no change in the SDK is needed.
buffer-size, num-request-handlers and the bind side of allow-tcp-reuse-address have no per-handler counterpart in the reactive stack: the transport and its selector threads are shared by every listener in the JVM. Either give them a meaning in LDAPConnectionHandler2, or state in the property descriptions of LDAPConnectionHandlerConfiguration.xml / Package.xml that LDAPConnectionHandler2 ignores them and name the system property that does the job. Which of the two is a decision for the fix.
Summary
org.forgerock.opendj.reactive.LDAPConnectionHandler2, the class every shipped LDAP listener runs on (opendj-server-legacy/resource/config/config.ldif, lines 316 and 340), never reads four properties of its own configuration:use-tcp-keep-alive,use-tcp-no-delay,buffer-sizeandnum-request-handlers. A fifth one,allow-tcp-reuse-address, is only half applied.dsconfigaccepts any value for them, the configuration entry stores it, and the handler keeps the same behaviour. Nothing in the configuration reference or in the server output tells the administrator that the setting has no effect.The legacy
org.opends.server.protocols.ldap.LDAPConnectionHandlerdoes apply all five, so the properties only work on the handler that no fresh install uses.Cause
LDAPConnectionHandler2.startListener()builds theLDAPListenerwith only two options taken from the configuration (LDAPConnectionHandler2.java, lines 683-685):None of
isUseTCPKeepAlive(),isUseTCPNoDelay(),getBufferSize()orgetNumRequestHandlers()is called anywhere in theorg.forgerock.opendj.reactivepackage.LDAPConnectionHandler2use-tcp-keep-alivesocket().setKeepAlive(...)on every accepted channel (LDAPConnectionHandler.java, line 1069)GrizzlyUtils.configureConnectionapplies the SDK optionSO_KEEPALIVE, defaulttrue(system propertyorg.forgerock.opendj.io.keepAlive)use-tcp-no-delaysocket().setTcpNoDelay(...)(line 1070)TCP_NO_DELAY, defaulttrue(system propertyorg.forgerock.opendj.io.tcpNoDelay)buffer-sizeLDAPClientConnection.java, lines 478-482)num-request-handlersLDAPConnectionHandler.java, line 619)ServerTCPNIOTransport, sized by the JVM-wide system propertyorg.forgerock.opendj.transport.selectorsallow-tcp-reuse-addresssetReuseAddress(...)on the listening channel (line 1044) and the "address in use" pre-checkLDAPConnectionHandler2.java, lines 503, 527 and 611-614); the listening socket is bound by the shared transport, whoseSO_REUSEADDRis enabled by default and can only be changed JVM-wide withorg.forgerock.opendj.transport.reuseAddress(ServerTCPNIOTransport.java, lines 94-99)Impact
use-tcp-keep-alive:falseoruse-tcp-no-delay:falsehas no effect on the default listeners: accepted sockets always getSO_KEEPALIVEandTCP_NODELAYswitched on.num-request-handlersis markedcomponent-restart, yet restarting the handler changes nothing; the thread count can only be changed for the whole JVM.buffer-sizechanges are silently dropped.allow-tcp-reuse-address:falsemakes the pre-check stricter, but the listening socket is still bound withSO_REUSEADDR.dsconfig create-connection-handler --type ldapwill also create handlers onLDAPConnectionHandler2, so the gap reaches every LDAP handler an administrator creates, not only the shipped ones.Steps to reproduce
Expected: the accepted socket has
SO_KEEPALIVEandTCP_NODELAYswitched off, as on a handler running the legacy class.Proposal
use-tcp-keep-aliveanduse-tcp-no-delay: pass them to the listener asLDAPListener.SO_KEEPALIVEandLDAPListener.TCP_NO_DELAY.LDAPServerFilteralready applies the listener options to every accepted connection throughGrizzlyUtils.configureConnection, so no change in the SDK is needed.buffer-size,num-request-handlersand the bind side ofallow-tcp-reuse-addresshave no per-handler counterpart in the reactive stack: the transport and its selector threads are shared by every listener in the JVM. Either give them a meaning inLDAPConnectionHandler2, or state in the property descriptions ofLDAPConnectionHandlerConfiguration.xml/Package.xmlthatLDAPConnectionHandler2ignores them and name the system property that does the job. Which of the two is a decision for the fix.