1 /* 2 * Copyright (c) 2018, 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.Closeable; 25 import java.io.InputStream; 26 import java.io.IOException; 27 import java.io.OutputStream; 28 import java.net.InetAddress; 29 import java.net.InetSocketAddress; 30 import java.net.ServerSocket; 31 import java.net.Socket; 32 import java.net.StandardProtocolFamily; 33 import java.util.ArrayList; 34 import java.util.Date; 35 import java.util.List; 36 import jdk.net.RdmaSockets; 37 38 import jdk.test.lib.Utils; 39 40 41 public class TestServers { 42 43 private TestServers() { } 44 45 /** 46 * An abstract server identifies a server which listens on a port on on a 47 * given machine. 48 */ 49 static abstract class AbstractServer { 50 51 private AbstractServer() { 52 } 53 54 public abstract int getPort(); 55 56 public abstract InetAddress getAddress(); 57 } 58 59 /** 60 * A downgraded type of AbstractServer which will refuse connections. Note: 61 * use it once and throw it away - this implementation opens an anonymous 62 * socket and closes it, returning the address of the closed socket. If 63 * other servers are started afterwards, the address/port might get reused 64 * and become connectable again - so it's not a good idea to assume that 65 * connections using this address/port will always be refused. Connections 66 * will be refused as long as the address/port of the refusing server has 67 * not been reused. 68 */ 69 static class RefusingServer extends AbstractServer { 70 71 final InetAddress address; 72 final int port; 73 74 private RefusingServer(InetAddress address, int port) { 75 this.address = address; 76 this.port = port; 77 } 78 79 @Override 80 public int getPort() { 81 return port; 82 } 83 84 @Override 85 public InetAddress getAddress() { 86 return address; 87 } 88 89 public static RefusingServer newRefusingServer() throws IOException { 90 return new RefusingServer(InetAddress.getLocalHost(), 91 Utils.refusingEndpoint().getPort()); 92 } 93 } 94 95 /** 96 * An abstract class for implementing small TCP servers for the nio tests 97 * purposes. Disclaimer: This is a naive implementation that uses the old 98 * networking APIs (not those from {@code java.nio.*}) and shamelessly 99 * extends/creates Threads instead of using an executor service. 100 */ 101 static abstract class AbstractTcpServer extends AbstractServer 102 implements Runnable, Closeable { 103 104 protected final long linger; // #of ms to wait before responding 105 private Thread acceptThread; // thread waiting for accept 106 // list of opened connections that should be closed on close. 107 private List<TcpConnectionThread> connections = new ArrayList<>(); 108 private ServerSocket serverSocket; // the server socket 109 private boolean started = false; // whether the server is started 110 Throwable error = null; 111 112 /** 113 * Creates a new abstract TCP server. 114 * 115 * @param linger the amount of time the server should wait before 116 * responding to requests. 117 */ 118 protected AbstractTcpServer(long linger) { 119 this.linger = linger; 120 } 121 122 /** 123 * The local port to which the server is bound. 124 * 125 * @return The local port to which the server is bound. 126 * @exception IllegalStateException is thrown if the server is not 127 * started. 128 */ 129 @Override 130 public final synchronized int getPort() { 131 if (!started) { 132 throw new IllegalStateException("Not started"); 133 } 134 return serverSocket.getLocalPort(); 135 } 136 137 /** 138 * The local address to which the server is bound. 139 * 140 * @return The local address to which the server is bound. 141 * @exception IllegalStateException is thrown if the server is not 142 * started. 143 */ 144 @Override 145 public final synchronized InetAddress getAddress() { 146 if (!started) { 147 throw new IllegalStateException("Not started"); 148 } 149 return serverSocket.getInetAddress(); 150 } 151 152 /** 153 * Tells whether the server is started. 154 * 155 * @return true if the server is started. 156 */ 157 public final synchronized boolean isStarted() { 158 return started; 159 } 160 161 /** 162 * Creates a new server socket. 163 * 164 * @param port local port to bind to. 165 * @param backlog requested maximum length of the queue of incoming 166 * connections. 167 * @param address local address to bind to. 168 * @return a new bound server socket ready to accept connections. 169 * @throws IOException if the socket cannot be created or bound. 170 */ 171 protected ServerSocket newServerSocket(int port, int backlog, 172 InetAddress address) 173 throws IOException { 174 ServerSocket ss = RdmaSockets.openServerSocket(StandardProtocolFamily.INET); 175 ss.bind(new InetSocketAddress(address, port), backlog); 176 return ss; 177 } 178 179 /** 180 * Starts listening for connections. 181 * 182 * @throws IOException if the server socket cannot be created or bound. 183 */ 184 public final synchronized void start() throws IOException { 185 if (started) { 186 return; 187 } 188 final ServerSocket socket = 189 newServerSocket(0, 100, InetAddress.getLocalHost()); 190 serverSocket = socket; 191 acceptThread = new Thread(this); 192 acceptThread.setDaemon(true); 193 acceptThread.start(); 194 started = true; 195 } 196 197 /** 198 * Calls {@code Thread.sleep(linger);} 199 */ 200 protected final void lingerIfRequired() { 201 if (linger > 0) { 202 try { 203 Thread.sleep(linger); 204 } catch (InterruptedException x) { 205 Thread.interrupted(); 206 final ServerSocket socket = serverSocket(); 207 if (socket != null && !socket.isClosed()) { 208 System.err.println("Thread interrupted..."); 209 } 210 } 211 } 212 } 213 214 final synchronized ServerSocket serverSocket() { 215 return this.serverSocket; 216 } 217 218 /** 219 * The main accept loop. 220 */ 221 @Override 222 public final void run() { 223 final ServerSocket sSocket = serverSocket(); 224 try { 225 Socket s; 226 while (isStarted() && !Thread.interrupted() 227 && (s = sSocket.accept()) != null) { 228 lingerIfRequired(); 229 listen(s); 230 } 231 } catch (Exception x) { 232 error = x; 233 } finally { 234 synchronized (this) { 235 if (!sSocket.isClosed()) { 236 try { 237 sSocket.close(); 238 } catch (IOException x) { 239 System.err.println("Failed to close server socket"); 240 } 241 } 242 if (started && this.serverSocket == sSocket) { 243 started = false; 244 this.serverSocket = null; 245 this.acceptThread = null; 246 } 247 } 248 } 249 } 250 251 /** 252 * Represents a connection accepted by the server. 253 */ 254 protected abstract class TcpConnectionThread extends Thread { 255 256 protected final Socket socket; 257 258 protected TcpConnectionThread(Socket socket) { 259 this.socket = socket; 260 this.setDaemon(true); 261 } 262 263 public void close() throws IOException { 264 socket.close(); 265 interrupt(); 266 } 267 } 268 269 /** 270 * Creates a new TcpConnnectionThread to handle the connection through 271 * an accepted socket. 272 * 273 * @param s the socket returned by {@code serverSocket.accept()}. 274 * @return a new TcpConnnectionThread to handle the connection through 275 * an accepted socket. 276 */ 277 protected abstract TcpConnectionThread createConnection(Socket s); 278 279 /** 280 * Creates and starts a new TcpConnectionThread to handle the accepted 281 * socket. 282 * 283 * @param s the socket returned by {@code serverSocket.accept()}. 284 */ 285 private synchronized void listen(Socket s) { 286 TcpConnectionThread c = createConnection(s); 287 c.start(); 288 addConnection(c); 289 } 290 291 /** 292 * Add the connection to the list of accepted connections. 293 * 294 * @param connection an accepted connection. 295 */ 296 protected synchronized void addConnection( 297 TcpConnectionThread connection) { 298 connections.add(connection); 299 } 300 301 /** 302 * Remove the connection from the list of accepted connections. 303 * 304 * @param connection an accepted connection. 305 */ 306 protected synchronized void removeConnection( 307 TcpConnectionThread connection) { 308 connections.remove(connection); 309 } 310 311 /** 312 * Close the server socket and all the connections present in the list 313 * of accepted connections. 314 * 315 * @throws IOException 316 */ 317 @Override 318 public synchronized void close() throws IOException { 319 if (serverSocket != null && !serverSocket.isClosed()) { 320 serverSocket.close(); 321 } 322 if (acceptThread != null) { 323 acceptThread.interrupt(); 324 } 325 int failed = 0; 326 for (TcpConnectionThread c : connections) { 327 try { 328 c.close(); 329 } catch (IOException x) { 330 // no matter - we're closing. 331 failed++; 332 } 333 } 334 connections.clear(); 335 if (failed > 0) { 336 throw new IOException("Failed to close some connections"); 337 } 338 } 339 } 340 341 /** 342 * A small TCP Server that emulates the echo service for tests purposes. See 343 * http://en.wikipedia.org/wiki/Echo_Protocol This server uses an anonymous 344 * port - NOT the standard port 7. We don't guarantee that its behavior 345 * exactly matches the RFC - the only purpose of this server is to have 346 * something that responds to nio tests... 347 */ 348 static class EchoServer extends AbstractTcpServer { 349 350 public EchoServer() { 351 this(0L); 352 } 353 354 public EchoServer(long linger) { 355 super(linger); 356 } 357 358 @Override 359 protected TcpConnectionThread createConnection(Socket s) { 360 return new EchoConnection(s); 361 } 362 363 private final class EchoConnection extends TcpConnectionThread { 364 365 public EchoConnection(Socket socket) { 366 super(socket); 367 } 368 369 @Override 370 public void run() { 371 try { 372 final InputStream is = socket.getInputStream(); 373 final OutputStream out = socket.getOutputStream(); 374 byte[] b = new byte[255]; 375 int n; 376 while ((n = is.read(b)) > 0) { 377 lingerIfRequired(); 378 out.write(b, 0, n); 379 } 380 } catch (IOException io) { 381 // fall through to finally 382 } finally { 383 if (!socket.isClosed()) { 384 try { 385 socket.close(); 386 } catch (IOException x) { 387 System.err.println( 388 "Failed to close echo connection socket"); 389 } 390 } 391 removeConnection(this); 392 } 393 } 394 } 395 396 public static EchoServer startNewServer() throws IOException { 397 return startNewServer(0); 398 } 399 400 public static EchoServer startNewServer(long linger) throws IOException { 401 final EchoServer echoServer = new EchoServer(linger); 402 echoServer.start(); 403 return echoServer; 404 } 405 } 406 407 408 /** 409 * A small TCP Server that accept connections but does not response to any input. 410 */ 411 static final class NoResponseServer extends EchoServer { 412 public NoResponseServer() { 413 super(Long.MAX_VALUE); 414 } 415 416 public static NoResponseServer startNewServer() throws IOException { 417 final NoResponseServer noResponseServer = new NoResponseServer(); 418 noResponseServer.start(); 419 return noResponseServer; 420 } 421 } 422 423 424 /** 425 * A small TCP server that emulates the Day & Time service for tests 426 * purposes. See http://en.wikipedia.org/wiki/Daytime_Protocol This server 427 * uses an anonymous port - NOT the standard port 13. We don't guarantee 428 * that its behavior exactly matches the RFC - the only purpose of this 429 * server is to have something that responds to nio tests... 430 */ 431 static final class DayTimeServer extends AbstractTcpServer { 432 433 public DayTimeServer() { 434 this(0L); 435 } 436 437 public DayTimeServer(long linger) { 438 super(linger); 439 } 440 441 @Override 442 protected TcpConnectionThread createConnection(Socket s) { 443 return new DayTimeServerConnection(s); 444 } 445 446 @Override 447 protected void addConnection(TcpConnectionThread connection) { 448 // do nothing - the connection just write the date and terminates. 449 } 450 451 @Override 452 protected void removeConnection(TcpConnectionThread connection) { 453 // do nothing - we're not adding connections to the list... 454 } 455 456 private final class DayTimeServerConnection extends TcpConnectionThread { 457 458 public DayTimeServerConnection(Socket socket) { 459 super(socket); 460 } 461 462 @Override 463 public void run() { 464 try { 465 final OutputStream out = socket.getOutputStream(); 466 lingerIfRequired(); 467 out.write(new Date(System.currentTimeMillis()) 468 .toString().getBytes("US-ASCII")); 469 out.flush(); 470 } catch (IOException io) { 471 // fall through to finally 472 } finally { 473 if (!socket.isClosed()) { 474 try { 475 socket.close(); 476 } catch (IOException x) { 477 System.err.println( 478 "Failed to close echo connection socket"); 479 } 480 } 481 } 482 } 483 } 484 485 public static DayTimeServer startNewServer() 486 throws IOException { 487 return startNewServer(0); 488 } 489 490 public static DayTimeServer startNewServer(long linger) 491 throws IOException { 492 final DayTimeServer daytimeServer = new DayTimeServer(linger); 493 daytimeServer.start(); 494 return daytimeServer; 495 } 496 } 497 }