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 }