/* * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions.
*/
/* * A bare-bones (testing aid) server for LDAP scenarios. * * Override the following methods to provide customized behavior * * * beforeAcceptingConnections * * beforeConnectionHandled * * handleRequest (or handleRequestEx) * * Instances of this class are safe for use by multiple threads.
*/ publicclass BaseLdapServer implements Closeable {
privatevoid acceptConnections() {
logger().log(INFO, "Server is accepting connections at port {0}",
getPort()); try {
beforeAcceptingConnections(); while (isRunning()) {
Socket socket = serverSocket.accept();
logger().log(INFO, "Accepted new connection at {0}", socket); synchronized (lock) { // Recheck if the server is still running // as someone has to close the `socket` if (isRunning()) {
socketList.add(socket);
} else {
closeSilently(socket);
}
}
connectionsPool.submit(() -> handleConnection(socket));
}
} catch (Throwable t) { if (isRunning()) { thrownew RuntimeException( "Unexpected exception while accepting connections", t);
}
} finally {
logger().log(INFO, "Server stopped accepting connections at port {0}",
getPort());
}
}
/* * Called once immediately preceding the server accepting connections. * * Override to customize the behavior.
*/ protectedvoid beforeAcceptingConnections() { }
/* * A "Template Method" describing how a connection (represented by a socket) * is handled. * * The socket is closed immediately before the method returns (normally or * abruptly).
*/ privatevoid handleConnection(Socket socket) { // No need to close socket's streams separately, they will be closed // automatically when `socket.close()` is called
beforeConnectionHandled(socket);
ConnWrapper connWrapper = new ConnWrapper(socket); try (socket) {
OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream(); byte[] inBuffer = newbyte[1024]; int count; byte[] request;
ByteArrayOutputStream buffer = new ByteArrayOutputStream(); int msgLen = -1;
// As inBuffer.length > 0, at least 1 byte is read while ((count = in.read(inBuffer)) > 0) {
buffer.write(inBuffer, 0, count); if (msgLen <= 0) {
msgLen = LdapMessage.getMessageLength(buffer.toByteArray());
}
if (connWrapper.getWrapper() != null) {
closeSilently(connWrapper.getWrapper());
}
}
/* * Called first thing in `handleConnection()`. * * Override to customize the behavior.
*/ protectedvoid beforeConnectionHandled(Socket socket) { /* empty */ }
/* * Called after an LDAP request has been read in `handleConnection()`. * * Override to customize the behavior.
*/ protectedvoid handleRequest(Socket socket,
LdapMessage request,
OutputStream out) throws IOException
{
logger().log(INFO, "Discarding message {0} from {1}. "
+ "Override {2}.handleRequest to change this behavior.",
request, socket, getClass().getName());
}
/* * Called after an LDAP request has been read in `handleConnection()`. * * Override to customize the behavior if you want to handle starttls * extended op, otherwise override handleRequest method instead. * * This is extended handleRequest method which provide possibility to * wrap current socket connection, that's necessary to handle starttls * extended request, here is sample code about how to wrap current socket * * switch (request.getOperation()) { * ...... * case EXTENDED_REQUEST: * if (new String(request.getMessage()).endsWith(STARTTLS_REQ_OID)) { * out.write(STARTTLS_RESPONSE); * SSLSocket sslSocket = (SSLSocket) sslSocketFactory * .createSocket(socket, null, socket.getLocalPort(), * false); * sslSocket.setUseClientMode(false); * connWrapper.setWrapper(sslSocket); * } * break; * ...... * }
*/ protectedvoid handleRequestEx(Socket socket,
LdapMessage request,
OutputStream out,
ConnWrapper connWrapper) throws IOException { // by default, just call handleRequest to keep compatibility
handleRequest(socket, request, out);
}
/* * To be used by subclasses.
*/ protectedfinal System.Logger logger() { return logger;
}
/* * Starts this server. May be called only once.
*/ public BaseLdapServer start() { synchronized (lock) { if (state != State.NEW) { thrownew IllegalStateException(state.toString());
}
state = State.STARTED;
logger().log(INFO, "Starting server at port {0}", getPort());
acceptingThread.start(); returnthis;
}
}
/* * Stops this server. * * May be called at any time, even before a call to `start()`. In the latter * case the subsequent call to `start()` will throw an exception. Repeated * calls to this method have no effect. * * Stops accepting new connections, interrupts the threads serving already * accepted connections and closes all the sockets.
*/
@Override publicvoid close() { synchronized (lock) { if (state == State.STOPPED) { return;
}
state = State.STOPPED;
logger().log(INFO, "Stopping server at port {0}", getPort());
acceptingThread.interrupt();
closeSilently(serverSocket); // It's important to signal an interruption so that overridden // methods have a chance to return if they use // interruption-sensitive blocking operations. However, blocked I/O // operations on the socket will NOT react on that, hence the socket // also has to be closed to propagate shutting down.
connectionsPool.shutdownNow();
socketList.forEach(BaseLdapServer.this::closeSilently);
}
}
/** * Returns the local port this server is listening at. * * This method can be called at any time. * * @return the port this server is listening at
*/ publicint getPort() { return serverSocket.getLocalPort();
}
/** * Returns the address this server is listening at. * * This method can be called at any time. * * @return the address
*/ public InetAddress getInetAddress() { return serverSocket.getInetAddress();
}
/* * Returns a flag to indicate whether this server is running or not. * * @return {@code true} if this server is running, {@code false} otherwise.
*/ publicboolean isRunning() { synchronized (lock) { return state == State.STARTED;
}
}
/* * To be used by subclasses.
*/ protectedfinalvoid closeSilently(Closeable resource) { try {
resource.close();
} catch (IOException ignored) { }
}
/* * To be used for handling starttls extended request
*/ protectedclass ConnWrapper { private Socket original; private Socket wrapper; privateboolean flag = false;
public ConnWrapper(Socket socket) {
original = socket;
}
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.