Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/test/jdk/com/sun/jndi/ldap/lib/BaseLdapServer.java
41155 views
1
/*
2
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation.
8
*
9
* This code is distributed in the hope that it will be useful, but WITHOUT
10
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12
* version 2 for more details (a copy is included in the LICENSE file that
13
* accompanied this code).
14
*
15
* You should have received a copy of the GNU General Public License version
16
* 2 along with this work; if not, write to the Free Software Foundation,
17
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18
*
19
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20
* or visit www.oracle.com if you need additional information or have any
21
* questions.
22
*/
23
24
import java.io.ByteArrayOutputStream;
25
import java.io.Closeable;
26
import java.io.IOException;
27
import java.io.InputStream;
28
import java.io.OutputStream;
29
import java.net.InetAddress;
30
import java.net.ServerSocket;
31
import java.net.Socket;
32
import java.util.ArrayList;
33
import java.util.Arrays;
34
import java.util.List;
35
import java.util.Objects;
36
import java.util.concurrent.ExecutorService;
37
import java.util.concurrent.Executors;
38
39
import static java.lang.System.Logger.Level.INFO;
40
41
/*
42
* A bare-bones (testing aid) server for LDAP scenarios.
43
*
44
* Override the following methods to provide customized behavior
45
*
46
* * beforeAcceptingConnections
47
* * beforeConnectionHandled
48
* * handleRequest (or handleRequestEx)
49
*
50
* Instances of this class are safe for use by multiple threads.
51
*/
52
public class BaseLdapServer implements Closeable {
53
54
private static final System.Logger logger = System.getLogger("BaseLdapServer");
55
56
private final Thread acceptingThread = new Thread(this::acceptConnections);
57
private final ServerSocket serverSocket;
58
private final List<Socket> socketList = new ArrayList<>();
59
private final ExecutorService connectionsPool;
60
61
private final Object lock = new Object();
62
/*
63
* 3-valued state to detect restarts and other programming errors.
64
*/
65
private State state = State.NEW;
66
67
private enum State {
68
NEW,
69
STARTED,
70
STOPPED
71
}
72
73
public BaseLdapServer() throws IOException {
74
this(new ServerSocket(0, 0, InetAddress.getLoopbackAddress()));
75
}
76
77
public BaseLdapServer(ServerSocket serverSocket) {
78
this.serverSocket = Objects.requireNonNull(serverSocket);
79
this.connectionsPool = Executors.newCachedThreadPool();
80
}
81
82
private void acceptConnections() {
83
logger().log(INFO, "Server is accepting connections at port {0}",
84
getPort());
85
try {
86
beforeAcceptingConnections();
87
while (isRunning()) {
88
Socket socket = serverSocket.accept();
89
logger().log(INFO, "Accepted new connection at {0}", socket);
90
synchronized (lock) {
91
// Recheck if the server is still running
92
// as someone has to close the `socket`
93
if (isRunning()) {
94
socketList.add(socket);
95
} else {
96
closeSilently(socket);
97
}
98
}
99
connectionsPool.submit(() -> handleConnection(socket));
100
}
101
} catch (Throwable t) {
102
if (isRunning()) {
103
throw new RuntimeException(
104
"Unexpected exception while accepting connections", t);
105
}
106
} finally {
107
logger().log(INFO, "Server stopped accepting connections at port {0}",
108
getPort());
109
}
110
}
111
112
/*
113
* Called once immediately preceding the server accepting connections.
114
*
115
* Override to customize the behavior.
116
*/
117
protected void beforeAcceptingConnections() { }
118
119
/*
120
* A "Template Method" describing how a connection (represented by a socket)
121
* is handled.
122
*
123
* The socket is closed immediately before the method returns (normally or
124
* abruptly).
125
*/
126
private void handleConnection(Socket socket) {
127
// No need to close socket's streams separately, they will be closed
128
// automatically when `socket.close()` is called
129
beforeConnectionHandled(socket);
130
ConnWrapper connWrapper = new ConnWrapper(socket);
131
try (socket) {
132
OutputStream out = socket.getOutputStream();
133
InputStream in = socket.getInputStream();
134
byte[] inBuffer = new byte[1024];
135
int count;
136
byte[] request;
137
138
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
139
int msgLen = -1;
140
141
// As inBuffer.length > 0, at least 1 byte is read
142
while ((count = in.read(inBuffer)) > 0) {
143
buffer.write(inBuffer, 0, count);
144
if (msgLen <= 0) {
145
msgLen = LdapMessage.getMessageLength(buffer.toByteArray());
146
}
147
148
if (msgLen > 0 && buffer.size() >= msgLen) {
149
if (buffer.size() > msgLen) {
150
byte[] tmpBuffer = buffer.toByteArray();
151
request = Arrays.copyOf(tmpBuffer, msgLen);
152
buffer.reset();
153
buffer.write(tmpBuffer, msgLen, tmpBuffer.length - msgLen);
154
} else {
155
request = buffer.toByteArray();
156
buffer.reset();
157
}
158
msgLen = -1;
159
} else {
160
logger.log(INFO, "Request message incomplete, " +
161
"bytes received {0}, expected {1}", buffer.size(), msgLen);
162
continue;
163
}
164
handleRequestEx(socket, new LdapMessage(request), out, connWrapper);
165
if (connWrapper.updateRequired()) {
166
var wrapper = connWrapper.getWrapper();
167
in = wrapper.getInputStream();
168
out = wrapper.getOutputStream();
169
connWrapper.clearFlag();
170
}
171
}
172
} catch (Throwable t) {
173
if (!isRunning()) {
174
logger.log(INFO, "Connection Handler exit {0}", t.getMessage());
175
} else {
176
t.printStackTrace();
177
}
178
}
179
180
if (connWrapper.getWrapper() != null) {
181
closeSilently(connWrapper.getWrapper());
182
}
183
}
184
185
/*
186
* Called first thing in `handleConnection()`.
187
*
188
* Override to customize the behavior.
189
*/
190
protected void beforeConnectionHandled(Socket socket) { /* empty */ }
191
192
/*
193
* Called after an LDAP request has been read in `handleConnection()`.
194
*
195
* Override to customize the behavior.
196
*/
197
protected void handleRequest(Socket socket,
198
LdapMessage request,
199
OutputStream out)
200
throws IOException
201
{
202
logger().log(INFO, "Discarding message {0} from {1}. "
203
+ "Override {2}.handleRequest to change this behavior.",
204
request, socket, getClass().getName());
205
}
206
207
/*
208
* Called after an LDAP request has been read in `handleConnection()`.
209
*
210
* Override to customize the behavior if you want to handle starttls
211
* extended op, otherwise override handleRequest method instead.
212
*
213
* This is extended handleRequest method which provide possibility to
214
* wrap current socket connection, that's necessary to handle starttls
215
* extended request, here is sample code about how to wrap current socket
216
*
217
* switch (request.getOperation()) {
218
* ......
219
* case EXTENDED_REQUEST:
220
* if (new String(request.getMessage()).endsWith(STARTTLS_REQ_OID)) {
221
* out.write(STARTTLS_RESPONSE);
222
* SSLSocket sslSocket = (SSLSocket) sslSocketFactory
223
* .createSocket(socket, null, socket.getLocalPort(),
224
* false);
225
* sslSocket.setUseClientMode(false);
226
* connWrapper.setWrapper(sslSocket);
227
* }
228
* break;
229
* ......
230
* }
231
*/
232
protected void handleRequestEx(Socket socket,
233
LdapMessage request,
234
OutputStream out,
235
ConnWrapper connWrapper)
236
throws IOException {
237
// by default, just call handleRequest to keep compatibility
238
handleRequest(socket, request, out);
239
}
240
241
/*
242
* To be used by subclasses.
243
*/
244
protected final System.Logger logger() {
245
return logger;
246
}
247
248
/*
249
* Starts this server. May be called only once.
250
*/
251
public BaseLdapServer start() {
252
synchronized (lock) {
253
if (state != State.NEW) {
254
throw new IllegalStateException(state.toString());
255
}
256
state = State.STARTED;
257
logger().log(INFO, "Starting server at port {0}", getPort());
258
acceptingThread.start();
259
return this;
260
}
261
}
262
263
/*
264
* Stops this server.
265
*
266
* May be called at any time, even before a call to `start()`. In the latter
267
* case the subsequent call to `start()` will throw an exception. Repeated
268
* calls to this method have no effect.
269
*
270
* Stops accepting new connections, interrupts the threads serving already
271
* accepted connections and closes all the sockets.
272
*/
273
@Override
274
public void close() {
275
synchronized (lock) {
276
if (state == State.STOPPED) {
277
return;
278
}
279
state = State.STOPPED;
280
logger().log(INFO, "Stopping server at port {0}", getPort());
281
acceptingThread.interrupt();
282
closeSilently(serverSocket);
283
// It's important to signal an interruption so that overridden
284
// methods have a chance to return if they use
285
// interruption-sensitive blocking operations. However, blocked I/O
286
// operations on the socket will NOT react on that, hence the socket
287
// also has to be closed to propagate shutting down.
288
connectionsPool.shutdownNow();
289
socketList.forEach(BaseLdapServer.this::closeSilently);
290
}
291
}
292
293
/**
294
* Returns the local port this server is listening at.
295
*
296
* This method can be called at any time.
297
*
298
* @return the port this server is listening at
299
*/
300
public int getPort() {
301
return serverSocket.getLocalPort();
302
}
303
304
/**
305
* Returns the address this server is listening at.
306
*
307
* This method can be called at any time.
308
*
309
* @return the address
310
*/
311
public InetAddress getInetAddress() {
312
return serverSocket.getInetAddress();
313
}
314
315
/*
316
* Returns a flag to indicate whether this server is running or not.
317
*
318
* @return {@code true} if this server is running, {@code false} otherwise.
319
*/
320
public boolean isRunning() {
321
synchronized (lock) {
322
return state == State.STARTED;
323
}
324
}
325
326
/*
327
* To be used by subclasses.
328
*/
329
protected final void closeSilently(Closeable resource) {
330
try {
331
resource.close();
332
} catch (IOException ignored) { }
333
}
334
335
/*
336
* To be used for handling starttls extended request
337
*/
338
protected class ConnWrapper {
339
private Socket original;
340
private Socket wrapper;
341
private boolean flag = false;
342
343
public ConnWrapper(Socket socket) {
344
original = socket;
345
}
346
347
public Socket getWrapper() {
348
return wrapper;
349
}
350
351
public void setWrapper(Socket wrapper) {
352
if (wrapper != null && wrapper != original) {
353
this.wrapper = wrapper;
354
flag = true;
355
}
356
}
357
358
public boolean updateRequired() {
359
return flag;
360
}
361
362
public void clearFlag() {
363
flag = false;
364
}
365
}
366
}
367
368