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