1 /* 2 * Copyright (c) 2012, 2016, 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 /* Test utility classes 25 * 26 */ 27 28 import java.io.*; 29 import java.net.*; 30 import java.util.ArrayList; 31 import java.util.Date; 32 import java.util.List; 33 34 35 public class TestServers { 36 37 private TestServers() { } 38 39 /** 40 * An abstract server identifies a server which listens on a port on on a 41 * given machine. 42 */ 43 static abstract class AbstractServer { 44 45 private AbstractServer() { 46 } 47 48 public abstract int getPort(); 49 50 public abstract InetAddress getAddress(); 51 } 52 53 /** 54 * A downgraded type of AbstractServer which will refuse connections. Note: 55 * use it once and throw it away - this implementation opens an anonymous 56 * socket and closes it, returning the address of the closed socket. If 57 * other servers are started afterwards, the address/port might get reused 58 * and become connectable again - so it's not a good idea to assume that 59 * connections using this address/port will always be refused. Connections 60 * will be refused as long as the address/port of the refusing server has 61 * not been reused. 62 */ 63 static class RefusingServer extends AbstractServer { 64 65 final InetAddress address; 66 final int port; 67 68 private RefusingServer(InetAddress address, int port) { 69 this.address = address; 70 this.port = port; 71 } 72 73 @Override 74 public int getPort() { 75 return port; 76 } 77 78 @Override 79 public InetAddress getAddress() { 80 return address; 81 } 82 83 public static RefusingServer newRefusingServer() throws IOException { 84 // The port 1 is reserved for TCPMUX(RFC 1078), which is seldom used, 85 // and it's not used on all the test platform through JPRT. 86 // So we choose to use it as a refusing "server"'s "listen" port, 87 // it's much more stable than "open->close a server socket". 88 return new RefusingServer(InetAddress.getLocalHost(), 1); 89 } 90 } 91 92 /** 93 * An abstract class for implementing small TCP servers for the nio tests 94 * purposes. Disclaimer: This is a naive implementation that uses the old 95 * networking APIs (not those from {@code java.nio.*}) and shamelessly 96 * extends/creates Threads instead of using an executor service. 97 */ 98 static abstract class AbstractTcpServer extends AbstractServer 99 implements Runnable, Closeable { 100 101 protected final long linger; // #of ms to wait before responding 102 private Thread acceptThread; // thread waiting for accept 103 // list of opened connections that should be closed on close. 104 private List<TcpConnectionThread> connections = new ArrayList<>(); 105 private ServerSocket serverSocket; // the server socket 106 private boolean started = false; // whether the server is started 107 Throwable error = null; 108 109 /** 110 * Creates a new abstract TCP server. 111 * 112 * @param linger the amount of time the server should wait before 113 * responding to requests. 114 */ 115 protected AbstractTcpServer(long linger) { 116 this.linger = linger; 117 } 118 119 /** 120 * The local port to which the server is bound. 121 * 122 * @return The local port to which the server is bound. 123 * @exception IllegalStateException is thrown if the server is not 124 * started. 125 */ 126 @Override 127 public final synchronized int getPort() { 128 if (!started) { 129 throw new IllegalStateException("Not started"); 130 } 131 return serverSocket.getLocalPort(); 132 } 133 134 /** 135 * The local address to which the server is bound. 136 * 137 * @return The local address to which the server is bound. 138 * @exception IllegalStateException is thrown if the server is not 139 * started. 140 */ 141 @Override 142 public final synchronized InetAddress getAddress() { 143 if (!started) { 144 throw new IllegalStateException("Not started"); 145 } 146 return serverSocket.getInetAddress(); 147 } 148 149 /** 150 * Tells whether the server is started. 151 * 152 * @return true if the server is started. 153 */ 154 public final synchronized boolean isStarted() { 155 return started; 156 } 157 158 /** 159 * Creates a new server socket. 160 * 161 * @param port local port to bind to. 162 * @param backlog requested maximum length of the queue of incoming 163 * connections. 164 * @param address local address to bind to. 165 * @return a new bound server socket ready to accept connections. 166 * @throws IOException if the socket cannot be created or bound. 167 */ 168 protected ServerSocket newServerSocket(int port, int backlog, 169 InetAddress address) 170 throws IOException { 171 return new ServerSocket(port, backlog, address); 172 } 173 174 /** 175 * Starts listening for connections. 176 * 177 * @throws IOException if the server socket cannot be created or bound. 178 */ 179 public final synchronized void start() throws IOException { 180 if (started) { 181 return; 182 } 183 final ServerSocket socket = 184 newServerSocket(0, 100, InetAddress.getLocalHost()); 185 serverSocket = socket; 186 acceptThread = new Thread(this); 187 acceptThread.setDaemon(true); 188 acceptThread.start(); 189 started = true; 190 } 191 192 /** 193 * Calls {@code Thread.sleep(linger);} 194 */ 195 protected final void lingerIfRequired() { 196 if (linger > 0) { 197 try { 198 Thread.sleep(linger); 199 } catch (InterruptedException x) { 200 Thread.interrupted(); 201 final ServerSocket socket = serverSocket(); 202 if (socket != null && !socket.isClosed()) { 203 System.err.println("Thread interrupted..."); 204 } 205 } 206 } 207 } 208 209 final synchronized ServerSocket serverSocket() { 210 return this.serverSocket; 211 } 212 213 /** 214 * The main accept loop. 215 */ 216 @Override 217 public final void run() { 218 final ServerSocket sSocket = serverSocket(); 219 try { 220 Socket s; 221 while (isStarted() && !Thread.interrupted() 222 && (s = sSocket.accept()) != null) { 223 lingerIfRequired(); 224 listen(s); 225 } 226 } catch (Exception x) { 227 error = x; 228 } finally { 229 synchronized (this) { 230 if (!sSocket.isClosed()) { 231 try { 232 sSocket.close(); 233 } catch (IOException x) { 234 System.err.println("Failed to close server socket"); 235 } 236 } 237 if (started && this.serverSocket == sSocket) { 238 started = false; 239 this.serverSocket = null; 240 this.acceptThread = null; 241 } 242 } 243 } 244 } 245 246 /** 247 * Represents a connection accepted by the server. 248 */ 249 protected abstract class TcpConnectionThread extends Thread { 250 251 protected final Socket socket; 252 253 protected TcpConnectionThread(Socket socket) { 254 this.socket = socket; 255 this.setDaemon(true); 256 } 257 258 public void close() throws IOException { 259 socket.close(); 260 interrupt(); 261 } 262 } 263 264 /** 265 * Creates a new TcpConnnectionThread to handle the connection through 266 * an accepted socket. 267 * 268 * @param s the socket returned by {@code serverSocket.accept()}. 269 * @return a new TcpConnnectionThread to handle the connection through 270 * an accepted socket. 271 */ 272 protected abstract TcpConnectionThread createConnection(Socket s); 273 274 /** 275 * Creates and starts a new TcpConnectionThread to handle the accepted 276 * socket. 277 * 278 * @param s the socket returned by {@code serverSocket.accept()}. 279 */ 280 private synchronized void listen(Socket s) { 281 TcpConnectionThread c = createConnection(s); 282 c.start(); 283 addConnection(c); 284 } 285 286 /** 287 * Add the connection to the list of accepted connections. 288 * 289 * @param connection an accepted connection. 290 */ 291 protected synchronized void addConnection( 292 TcpConnectionThread connection) { 293 connections.add(connection); 294 } 295 296 /** 297 * Remove the connection from the list of accepted connections. 298 * 299 * @param connection an accepted connection. 300 */ 301 protected synchronized void removeConnection( 302 TcpConnectionThread connection) { 303 connections.remove(connection); 304 } 305 306 /** 307 * Close the server socket and all the connections present in the list 308 * of accepted connections. 309 * 310 * @throws IOException 311 */ 312 @Override 313 public synchronized void close() throws IOException { 314 if (serverSocket != null && !serverSocket.isClosed()) { 315 serverSocket.close(); 316 } 317 if (acceptThread != null) { 318 acceptThread.interrupt(); 319 } 320 int failed = 0; 321 for (TcpConnectionThread c : connections) { 322 try { 323 c.close(); 324 } catch (IOException x) { 325 // no matter - we're closing. 326 failed++; 327 } 328 } 329 connections.clear(); 330 if (failed > 0) { 331 throw new IOException("Failed to close some connections"); 332 } 333 } 334 } 335 336 /** 337 * A small TCP Server that emulates the echo service for tests purposes. See 338 * http://en.wikipedia.org/wiki/Echo_Protocol This server uses an anonymous 339 * port - NOT the standard port 7. We don't guarantee that its behavior 340 * exactly matches the RFC - the only purpose of this server is to have 341 * something that responds to nio tests... 342 */ 343 static class EchoServer extends AbstractTcpServer { 344 345 public EchoServer() { 346 this(0L); 347 } 348 349 public EchoServer(long linger) { 350 super(linger); 351 } 352 353 @Override 354 protected TcpConnectionThread createConnection(Socket s) { 355 return new EchoConnection(s); 356 } 357 358 private final class EchoConnection extends TcpConnectionThread { 359 360 public EchoConnection(Socket socket) { 361 super(socket); 362 } 363 364 @Override 365 public void run() { 366 try { 367 final InputStream is = socket.getInputStream(); 368 final OutputStream out = socket.getOutputStream(); 369 byte[] b = new byte[255]; 370 int n; 371 while ((n = is.read(b)) > 0) { 372 lingerIfRequired(); 373 out.write(b, 0, n); 374 } 375 } catch (IOException io) { 376 // fall through to finally 377 } finally { 378 if (!socket.isClosed()) { 379 try { 380 socket.close(); 381 } catch (IOException x) { 382 System.err.println( 383 "Failed to close echo connection socket"); 384 } 385 } 386 removeConnection(this); 387 } 388 } 389 } 390 391 public static EchoServer startNewServer() throws IOException { 392 return startNewServer(0); 393 } 394 395 public static EchoServer startNewServer(long linger) throws IOException { 396 final EchoServer echoServer = new EchoServer(linger); 397 echoServer.start(); 398 return echoServer; 399 } 400 } 401 402 /** 403 * A small TCP Server that accept connections but does not response to any input. 404 */ 405 static final class NoResponseServer extends EchoServer { 406 public NoResponseServer() { 407 super(Long.MAX_VALUE); 408 } 409 410 public static NoResponseServer startNewServer() throws IOException { 411 final NoResponseServer noResponseServer = new NoResponseServer(); 412 noResponseServer.start(); 413 return noResponseServer; 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 491 /** 492 * An abstract class for implementing small UDP Servers for the nio tests 493 * purposes. Disclaimer: This is a naive implementation that uses the old 494 * networking APIs (not those from {@code java.nio.*}) and shamelessly 495 * extends/creates Threads instead of using an executor service. 496 */ 497 static abstract class AbstractUdpServer extends AbstractServer 498 implements Runnable, Closeable { 499 500 protected final long linger; // #of ms to wait before responding 501 private Thread acceptThread; // thread waiting for packets 502 private DatagramSocket serverSocket; // the server socket 503 private boolean started = false; // whether the server is started 504 Throwable error = null; 505 506 /** 507 * Creates a new abstract UDP server. 508 * 509 * @param linger the amount of time the server should wait before 510 * responding to requests. 511 */ 512 protected AbstractUdpServer(long linger) { 513 this.linger = linger; 514 } 515 516 /** 517 * The local port to which the server is bound. 518 * 519 * @return The local port to which the server is bound. 520 * @exception IllegalStateException is thrown if the server is not 521 * started. 522 */ 523 @Override 524 public final synchronized int getPort() { 525 if (!started) { 526 throw new IllegalStateException("Not started"); 527 } 528 return serverSocket.getLocalPort(); 529 } 530 531 /** 532 * The local address to which the server is bound. 533 * 534 * @return The local address to which the server is bound. 535 * @exception IllegalStateException is thrown if the server is not 536 * started. 537 */ 538 @Override 539 public final synchronized InetAddress getAddress() { 540 if (!started) { 541 throw new IllegalStateException("Not started"); 542 } 543 return serverSocket.getLocalAddress(); 544 } 545 546 /** 547 * Tells whether the server is started. 548 * 549 * @return true if the server is started. 550 */ 551 public final synchronized boolean isStarted() { 552 return started; 553 } 554 555 /** 556 * Creates a new datagram socket. 557 * 558 * @param port local port to bind to. 559 * @param address local address to bind to. 560 * @return a new bound server socket ready to listen for packets. 561 * @throws IOException if the socket cannot be created or bound. 562 */ 563 protected DatagramSocket newDatagramSocket(int port, 564 InetAddress address) 565 throws IOException { 566 return new DatagramSocket(port, address); 567 } 568 569 /** 570 * Starts listening for connections. 571 * 572 * @throws IOException if the server socket cannot be created or bound. 573 */ 574 public final synchronized void start() throws IOException { 575 if (started) { 576 return; 577 } 578 final DatagramSocket socket = 579 newDatagramSocket(0, InetAddress.getLocalHost()); 580 serverSocket = socket; 581 acceptThread = new Thread(this); 582 acceptThread.setDaemon(true); 583 acceptThread.start(); 584 started = true; 585 } 586 587 /** 588 * Calls {@code Thread.sleep(linger);} 589 */ 590 protected final void lingerIfRequired() { 591 if (linger > 0) { 592 try { 593 Thread.sleep(linger); 594 } catch (InterruptedException x) { 595 Thread.interrupted(); 596 final DatagramSocket socket = serverSocket(); 597 if (socket != null && !socket.isClosed()) { 598 System.err.println("Thread interrupted..."); 599 } 600 } 601 } 602 } 603 604 final synchronized DatagramSocket serverSocket() { 605 return this.serverSocket; 606 } 607 608 final synchronized boolean send(DatagramSocket socket, 609 DatagramPacket response) throws IOException { 610 if (!socket.isClosed()) { 611 socket.send(response); 612 return true; 613 } else { 614 return false; 615 } 616 } 617 618 /** 619 * The main receive loop. 620 */ 621 @Override 622 public final void run() { 623 final DatagramSocket sSocket = serverSocket(); 624 try { 625 final int size = Math.max(1024, sSocket.getReceiveBufferSize()); 626 if (size > sSocket.getReceiveBufferSize()) { 627 sSocket.setReceiveBufferSize(size); 628 } 629 while (isStarted() && !Thread.interrupted() && !sSocket.isClosed()) { 630 final byte[] buf = new byte[size]; 631 final DatagramPacket packet = 632 new DatagramPacket(buf, buf.length); 633 lingerIfRequired(); 634 sSocket.receive(packet); 635 //System.out.println("Received packet from: " 636 // + packet.getAddress()+":"+packet.getPort()); 637 handle(sSocket, packet); 638 } 639 } catch (Exception x) { 640 error = x; 641 } finally { 642 synchronized (this) { 643 if (!sSocket.isClosed()) { 644 sSocket.close(); 645 } 646 if (started && this.serverSocket == sSocket) { 647 started = false; 648 this.serverSocket = null; 649 this.acceptThread = null; 650 } 651 } 652 } 653 } 654 655 /** 656 * Represents an UDP request received by the server. 657 */ 658 protected abstract class UdpRequestThread extends Thread { 659 660 protected final DatagramPacket request; 661 protected final DatagramSocket socket; 662 663 protected UdpRequestThread(DatagramSocket socket, DatagramPacket request) { 664 this.socket = socket; 665 this.request = request; 666 this.setDaemon(true); 667 } 668 } 669 670 /** 671 * Creates a new UdpRequestThread to handle a DatagramPacket received 672 * through a DatagramSocket. 673 * 674 * @param socket the socket through which the request was received. 675 * @param request the datagram packet received through the socket. 676 * @return a new UdpRequestThread to handle the request received through 677 * a DatagramSocket. 678 */ 679 protected abstract UdpRequestThread createConnection(DatagramSocket socket, 680 DatagramPacket request); 681 682 /** 683 * Creates and starts a new UdpRequestThread to handle the received 684 * datagram packet. 685 * 686 * @param socket the socket through which the request was received. 687 * @param request the datagram packet received through the socket. 688 */ 689 private synchronized void handle(DatagramSocket socket, 690 DatagramPacket request) { 691 UdpRequestThread c = createConnection(socket, request); 692 // c can be null if the request requires no response. 693 if (c != null) { 694 c.start(); 695 } 696 } 697 698 /** 699 * Close the server socket. 700 * 701 * @throws IOException 702 */ 703 @Override 704 public synchronized void close() throws IOException { 705 if (serverSocket != null && !serverSocket.isClosed()) { 706 serverSocket.close(); 707 } 708 if (acceptThread != null) { 709 acceptThread.interrupt(); 710 } 711 } 712 } 713 714 /** 715 * A small UDP Server that emulates the discard service for tests purposes. 716 * See http://en.wikipedia.org/wiki/Discard_Protocol This server uses an 717 * anonymous port - NOT the standard port 9. We don't guarantee that its 718 * behavior exactly matches the RFC - the only purpose of this server is to 719 * have something that responds to nio tests... 720 */ 721 static final class UdpDiscardServer extends AbstractUdpServer { 722 723 public UdpDiscardServer() { 724 this(0L); 725 } 726 727 public UdpDiscardServer(long linger) { 728 super(linger); 729 } 730 731 @Override 732 protected UdpRequestThread createConnection(DatagramSocket socket, 733 DatagramPacket request) { 734 // no response required 735 return null; 736 } 737 738 public static UdpDiscardServer startNewServer() throws IOException { 739 return startNewServer(0); 740 } 741 742 public static UdpDiscardServer startNewServer(long linger) throws IOException { 743 final UdpDiscardServer discardServer = new UdpDiscardServer(linger); 744 discardServer.start(); 745 return discardServer; 746 } 747 } 748 749 /** 750 * A small UDP Server that emulates the echo service for tests purposes. See 751 * http://en.wikipedia.org/wiki/Echo_Protocol This server uses an anonymous 752 * port - NOT the standard port 7. We don't guarantee that its behavior 753 * exactly matches the RFC - the only purpose of this server is to have 754 * something that responds to nio tests... 755 */ 756 static final class UdpEchoServer extends AbstractUdpServer { 757 758 public UdpEchoServer() { 759 this(0L); 760 } 761 762 public UdpEchoServer(long linger) { 763 super(linger); 764 } 765 766 @Override 767 protected UdpEchoRequest createConnection(DatagramSocket socket, 768 DatagramPacket request) { 769 return new UdpEchoRequest(socket, request); 770 } 771 772 private final class UdpEchoRequest extends UdpRequestThread { 773 774 public UdpEchoRequest(DatagramSocket socket, DatagramPacket request) { 775 super(socket, request); 776 } 777 778 @Override 779 public void run() { 780 try { 781 lingerIfRequired(); 782 final DatagramPacket response = 783 new DatagramPacket(request.getData(), 784 request.getOffset(), request.getLength(), 785 request.getAddress(), request.getPort()); 786 send(socket, response); 787 } catch (IOException io) { 788 System.err.println("Failed to send response: " + io); 789 io.printStackTrace(System.err); 790 } 791 } 792 } 793 794 public static UdpEchoServer startNewServer() throws IOException { 795 return startNewServer(0); 796 } 797 798 public static UdpEchoServer startNewServer(long linger) throws IOException { 799 final UdpEchoServer echoServer = new UdpEchoServer(linger); 800 echoServer.start(); 801 return echoServer; 802 } 803 } 804 805 /** 806 * A small UDP server that emulates the Day & Time service for tests 807 * purposes. See http://en.wikipedia.org/wiki/Daytime_Protocol This server 808 * uses an anonymous port - NOT the standard port 13. We don't guarantee 809 * that its behavior exactly matches the RFC - the only purpose of this 810 * server is to have something that responds to nio tests... 811 */ 812 static final class UdpDayTimeServer extends AbstractUdpServer { 813 814 public UdpDayTimeServer() { 815 this(0L); 816 } 817 818 public UdpDayTimeServer(long linger) { 819 super(linger); 820 } 821 822 @Override 823 protected UdpDayTimeRequestThread createConnection(DatagramSocket socket, 824 DatagramPacket request) { 825 return new UdpDayTimeRequestThread(socket, request); 826 } 827 828 private final class UdpDayTimeRequestThread extends UdpRequestThread { 829 830 public UdpDayTimeRequestThread(DatagramSocket socket, 831 DatagramPacket request) { 832 super(socket, request); 833 } 834 835 @Override 836 public void run() { 837 try { 838 lingerIfRequired(); 839 final byte[] data = new Date(System.currentTimeMillis()) 840 .toString().getBytes("US-ASCII"); 841 final DatagramPacket response = 842 new DatagramPacket(data, 0, data.length, 843 request.getAddress(), request.getPort()); 844 send(socket, response); 845 } catch (IOException io) { 846 System.err.println("Failed to send response: " + io); 847 io.printStackTrace(System.err); 848 } 849 } 850 } 851 852 public static UdpDayTimeServer startNewServer() throws IOException { 853 return startNewServer(0); 854 } 855 856 public static UdpDayTimeServer startNewServer(long linger) 857 throws IOException { 858 final UdpDayTimeServer echoServer = new UdpDayTimeServer(linger); 859 echoServer.start(); 860 return echoServer; 861 } 862 } 863 }