Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/test/java/nio/channels/TestServers.java
38813 views
/*1* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/* Test utility classes24*25*/2627import java.io.*;28import java.net.*;29import java.util.ArrayList;30import java.util.Date;31import java.util.List;323334public class TestServers {3536private TestServers() { }3738/**39* An abstract server identifies a server which listens on a port on on a40* given machine.41*/42static abstract class AbstractServer {4344private AbstractServer() {45}4647public abstract int getPort();4849public abstract InetAddress getAddress();50}5152/**53* A downgraded type of AbstractServer which will refuse connections. Note:54* use it once and throw it away - this implementation opens an anonymous55* socket and closes it, returning the address of the closed socket. If56* other servers are started afterwards, the address/port might get reused57* and become connectable again - so it's not a good idea to assume that58* connections using this address/port will always be refused. Connections59* will be refused as long as the address/port of the refusing server has60* not been reused.61*/62static class RefusingServer extends AbstractServer {6364final InetAddress address;65final int port;6667private RefusingServer(InetAddress address, int port) {68this.address = address;69this.port = port;70}7172@Override73public int getPort() {74return port;75}7677@Override78public InetAddress getAddress() {79return address;80}8182public static RefusingServer startNewServer() throws IOException {83ServerSocket socket = new ServerSocket(0, 100,84InetAddress.getLocalHost());85RefusingServer server = new RefusingServer(socket.getInetAddress(),86socket.getLocalPort());87socket.close();88return server;89}90}9192/**93* An abstract class for implementing small TCP servers for the nio tests94* purposes. Disclaimer: This is a naive implementation that uses the old95* networking APIs (not those from {@code java.nio.*}) and shamelessly96* extends/creates Threads instead of using an executor service.97*/98static abstract class AbstractTcpServer extends AbstractServer99implements Runnable, Closeable {100101protected final long linger; // #of ms to wait before responding102private Thread acceptThread; // thread waiting for accept103// list of opened connections that should be closed on close.104private List<TcpConnectionThread> connections = new ArrayList<>();105private ServerSocket serverSocket; // the server socket106private boolean started = false; // whether the server is started107Throwable error = null;108109/**110* Creates a new abstract TCP server.111*112* @param linger the amount of time the server should wait before113* responding to requests.114*/115protected AbstractTcpServer(long linger) {116this.linger = linger;117}118119/**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 not124* started.125*/126@Override127public final synchronized int getPort() {128if (!started) {129throw new IllegalStateException("Not started");130}131return serverSocket.getLocalPort();132}133134/**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 not139* started.140*/141@Override142public final synchronized InetAddress getAddress() {143if (!started) {144throw new IllegalStateException("Not started");145}146return serverSocket.getInetAddress();147}148149/**150* Tells whether the server is started.151*152* @return true if the server is started.153*/154public final synchronized boolean isStarted() {155return started;156}157158/**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 incoming163* 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*/168protected ServerSocket newServerSocket(int port, int backlog,169InetAddress address)170throws IOException {171return new ServerSocket(port, backlog, address);172}173174/**175* Starts listening for connections.176*177* @throws IOException if the server socket cannot be created or bound.178*/179public final synchronized void start() throws IOException {180if (started) {181return;182}183final ServerSocket socket =184newServerSocket(0, 100, InetAddress.getLocalHost());185serverSocket = socket;186acceptThread = new Thread(this);187acceptThread.setDaemon(true);188acceptThread.start();189started = true;190}191192/**193* Calls {@code Thread.sleep(linger);}194*/195protected final void lingerIfRequired() {196if (linger > 0) {197try {198Thread.sleep(linger);199} catch (InterruptedException x) {200Thread.interrupted();201final ServerSocket socket = serverSocket();202if (socket != null && !socket.isClosed()) {203System.err.println("Thread interrupted...");204}205}206}207}208209final synchronized ServerSocket serverSocket() {210return this.serverSocket;211}212213/**214* The main accept loop.215*/216@Override217public final void run() {218final ServerSocket sSocket = serverSocket();219try {220Socket s;221while (isStarted() && !Thread.interrupted()222&& (s = sSocket.accept()) != null) {223lingerIfRequired();224listen(s);225}226} catch (Exception x) {227error = x;228} finally {229synchronized (this) {230if (!sSocket.isClosed()) {231try {232sSocket.close();233} catch (IOException x) {234System.err.println("Failed to close server socket");235}236}237if (started && this.serverSocket == sSocket) {238started = false;239this.serverSocket = null;240this.acceptThread = null;241}242}243}244}245246/**247* Represents a connection accepted by the server.248*/249protected abstract class TcpConnectionThread extends Thread {250251protected final Socket socket;252253protected TcpConnectionThread(Socket socket) {254this.socket = socket;255this.setDaemon(true);256}257258public void close() throws IOException {259socket.close();260interrupt();261}262}263264/**265* Creates a new TcpConnnectionThread to handle the connection through266* an accepted socket.267*268* @param s the socket returned by {@code serverSocket.accept()}.269* @return a new TcpConnnectionThread to handle the connection through270* an accepted socket.271*/272protected abstract TcpConnectionThread createConnection(Socket s);273274/**275* Creates and starts a new TcpConnectionThread to handle the accepted276* socket.277*278* @param s the socket returned by {@code serverSocket.accept()}.279*/280private synchronized void listen(Socket s) {281TcpConnectionThread c = createConnection(s);282c.start();283addConnection(c);284}285286/**287* Add the connection to the list of accepted connections.288*289* @param connection an accepted connection.290*/291protected synchronized void addConnection(292TcpConnectionThread connection) {293connections.add(connection);294}295296/**297* Remove the connection from the list of accepted connections.298*299* @param connection an accepted connection.300*/301protected synchronized void removeConnection(302TcpConnectionThread connection) {303connections.remove(connection);304}305306/**307* Close the server socket and all the connections present in the list308* of accepted connections.309*310* @throws IOException311*/312@Override313public synchronized void close() throws IOException {314if (serverSocket != null && !serverSocket.isClosed()) {315serverSocket.close();316}317if (acceptThread != null) {318acceptThread.interrupt();319}320int failed = 0;321for (TcpConnectionThread c : connections) {322try {323c.close();324} catch (IOException x) {325// no matter - we're closing.326failed++;327}328}329connections.clear();330if (failed > 0) {331throw new IOException("Failed to close some connections");332}333}334}335336/**337* A small TCP Server that emulates the echo service for tests purposes. See338* http://en.wikipedia.org/wiki/Echo_Protocol This server uses an anonymous339* port - NOT the standard port 7. We don't guarantee that its behavior340* exactly matches the RFC - the only purpose of this server is to have341* something that responds to nio tests...342*/343static final class EchoServer extends AbstractTcpServer {344345public EchoServer() {346this(0L);347}348349public EchoServer(long linger) {350super(linger);351}352353@Override354protected TcpConnectionThread createConnection(Socket s) {355return new EchoConnection(s);356}357358private final class EchoConnection extends TcpConnectionThread {359360public EchoConnection(Socket socket) {361super(socket);362}363364@Override365public void run() {366try {367final InputStream is = socket.getInputStream();368final OutputStream out = socket.getOutputStream();369byte[] b = new byte[255];370int n;371while ((n = is.read(b)) > 0) {372lingerIfRequired();373out.write(b, 0, n);374}375} catch (IOException io) {376// fall through to finally377} finally {378if (!socket.isClosed()) {379try {380socket.close();381} catch (IOException x) {382System.err.println(383"Failed to close echo connection socket");384}385}386removeConnection(this);387}388}389}390391public static EchoServer startNewServer() throws IOException {392return startNewServer(0);393}394395public static EchoServer startNewServer(long linger) throws IOException {396final EchoServer echoServer = new EchoServer(linger);397echoServer.start();398return echoServer;399}400}401402/**403* A small TCP server that emulates the Day & Time service for tests404* purposes. See http://en.wikipedia.org/wiki/Daytime_Protocol This server405* uses an anonymous port - NOT the standard port 13. We don't guarantee406* that its behavior exactly matches the RFC - the only purpose of this407* server is to have something that responds to nio tests...408*/409static final class DayTimeServer extends AbstractTcpServer {410411public DayTimeServer() {412this(0L);413}414415public DayTimeServer(long linger) {416super(linger);417}418419@Override420protected TcpConnectionThread createConnection(Socket s) {421return new DayTimeServerConnection(s);422}423424@Override425protected void addConnection(TcpConnectionThread connection) {426// do nothing - the connection just write the date and terminates.427}428429@Override430protected void removeConnection(TcpConnectionThread connection) {431// do nothing - we're not adding connections to the list...432}433434private final class DayTimeServerConnection extends TcpConnectionThread {435436public DayTimeServerConnection(Socket socket) {437super(socket);438}439440@Override441public void run() {442try {443final OutputStream out = socket.getOutputStream();444lingerIfRequired();445out.write(new Date(System.currentTimeMillis())446.toString().getBytes("US-ASCII"));447out.flush();448} catch (IOException io) {449// fall through to finally450} finally {451if (!socket.isClosed()) {452try {453socket.close();454} catch (IOException x) {455System.err.println(456"Failed to close echo connection socket");457}458}459}460}461}462463public static DayTimeServer startNewServer()464throws IOException {465return startNewServer(0);466}467468public static DayTimeServer startNewServer(long linger)469throws IOException {470final DayTimeServer daytimeServer = new DayTimeServer(linger);471daytimeServer.start();472return daytimeServer;473}474}475476/**477* An abstract class for implementing small UDP Servers for the nio tests478* purposes. Disclaimer: This is a naive implementation that uses the old479* networking APIs (not those from {@code java.nio.*}) and shamelessly480* extends/creates Threads instead of using an executor service.481*/482static abstract class AbstractUdpServer extends AbstractServer483implements Runnable, Closeable {484485protected final long linger; // #of ms to wait before responding486private Thread acceptThread; // thread waiting for packets487private DatagramSocket serverSocket; // the server socket488private boolean started = false; // whether the server is started489Throwable error = null;490491/**492* Creates a new abstract UDP server.493*494* @param linger the amount of time the server should wait before495* responding to requests.496*/497protected AbstractUdpServer(long linger) {498this.linger = linger;499}500501/**502* The local port to which the server is bound.503*504* @return The local port to which the server is bound.505* @exception IllegalStateException is thrown if the server is not506* started.507*/508@Override509public final synchronized int getPort() {510if (!started) {511throw new IllegalStateException("Not started");512}513return serverSocket.getLocalPort();514}515516/**517* The local address to which the server is bound.518*519* @return The local address to which the server is bound.520* @exception IllegalStateException is thrown if the server is not521* started.522*/523@Override524public final synchronized InetAddress getAddress() {525if (!started) {526throw new IllegalStateException("Not started");527}528return serverSocket.getLocalAddress();529}530531/**532* Tells whether the server is started.533*534* @return true if the server is started.535*/536public final synchronized boolean isStarted() {537return started;538}539540/**541* Creates a new datagram socket.542*543* @param port local port to bind to.544* @param address local address to bind to.545* @return a new bound server socket ready to listen for packets.546* @throws IOException if the socket cannot be created or bound.547*/548protected DatagramSocket newDatagramSocket(int port,549InetAddress address)550throws IOException {551return new DatagramSocket(port, address);552}553554/**555* Starts listening for connections.556*557* @throws IOException if the server socket cannot be created or bound.558*/559public final synchronized void start() throws IOException {560if (started) {561return;562}563final DatagramSocket socket =564newDatagramSocket(0, InetAddress.getLocalHost());565serverSocket = socket;566acceptThread = new Thread(this);567acceptThread.setDaemon(true);568acceptThread.start();569started = true;570}571572/**573* Calls {@code Thread.sleep(linger);}574*/575protected final void lingerIfRequired() {576if (linger > 0) {577try {578Thread.sleep(linger);579} catch (InterruptedException x) {580Thread.interrupted();581final DatagramSocket socket = serverSocket();582if (socket != null && !socket.isClosed()) {583System.err.println("Thread interrupted...");584}585}586}587}588589final synchronized DatagramSocket serverSocket() {590return this.serverSocket;591}592593final synchronized boolean send(DatagramSocket socket,594DatagramPacket response) throws IOException {595if (!socket.isClosed()) {596socket.send(response);597return true;598} else {599return false;600}601}602603/**604* The main receive loop.605*/606@Override607public final void run() {608final DatagramSocket sSocket = serverSocket();609try {610final int size = Math.max(1024, sSocket.getReceiveBufferSize());611if (size > sSocket.getReceiveBufferSize()) {612sSocket.setReceiveBufferSize(size);613}614while (isStarted() && !Thread.interrupted() && !sSocket.isClosed()) {615final byte[] buf = new byte[size];616final DatagramPacket packet =617new DatagramPacket(buf, buf.length);618lingerIfRequired();619sSocket.receive(packet);620//System.out.println("Received packet from: "621// + packet.getAddress()+":"+packet.getPort());622handle(sSocket, packet);623}624} catch (Exception x) {625error = x;626} finally {627synchronized (this) {628if (!sSocket.isClosed()) {629sSocket.close();630}631if (started && this.serverSocket == sSocket) {632started = false;633this.serverSocket = null;634this.acceptThread = null;635}636}637}638}639640/**641* Represents an UDP request received by the server.642*/643protected abstract class UdpRequestThread extends Thread {644645protected final DatagramPacket request;646protected final DatagramSocket socket;647648protected UdpRequestThread(DatagramSocket socket, DatagramPacket request) {649this.socket = socket;650this.request = request;651this.setDaemon(true);652}653}654655/**656* Creates a new UdpRequestThread to handle a DatagramPacket received657* through a DatagramSocket.658*659* @param socket the socket through which the request was received.660* @param request the datagram packet received through the socket.661* @return a new UdpRequestThread to handle the request received through662* a DatagramSocket.663*/664protected abstract UdpRequestThread createConnection(DatagramSocket socket,665DatagramPacket request);666667/**668* Creates and starts a new UdpRequestThread to handle the received669* datagram packet.670*671* @param socket the socket through which the request was received.672* @param request the datagram packet received through the socket.673*/674private synchronized void handle(DatagramSocket socket,675DatagramPacket request) {676UdpRequestThread c = createConnection(socket, request);677// c can be null if the request requires no response.678if (c != null) {679c.start();680}681}682683/**684* Close the server socket.685*686* @throws IOException687*/688@Override689public synchronized void close() throws IOException {690if (serverSocket != null && !serverSocket.isClosed()) {691serverSocket.close();692}693if (acceptThread != null) {694acceptThread.interrupt();695}696}697}698699/**700* A small UDP Server that emulates the discard service for tests purposes.701* See http://en.wikipedia.org/wiki/Discard_Protocol This server uses an702* anonymous port - NOT the standard port 9. We don't guarantee that its703* behavior exactly matches the RFC - the only purpose of this server is to704* have something that responds to nio tests...705*/706static final class UdpDiscardServer extends AbstractUdpServer {707708public UdpDiscardServer() {709this(0L);710}711712public UdpDiscardServer(long linger) {713super(linger);714}715716@Override717protected UdpRequestThread createConnection(DatagramSocket socket,718DatagramPacket request) {719// no response required720return null;721}722723public static UdpDiscardServer startNewServer() throws IOException {724return startNewServer(0);725}726727public static UdpDiscardServer startNewServer(long linger) throws IOException {728final UdpDiscardServer discardServer = new UdpDiscardServer(linger);729discardServer.start();730return discardServer;731}732}733734/**735* A small UDP Server that emulates the echo service for tests purposes. See736* http://en.wikipedia.org/wiki/Echo_Protocol This server uses an anonymous737* port - NOT the standard port 7. We don't guarantee that its behavior738* exactly matches the RFC - the only purpose of this server is to have739* something that responds to nio tests...740*/741static final class UdpEchoServer extends AbstractUdpServer {742743public UdpEchoServer() {744this(0L);745}746747public UdpEchoServer(long linger) {748super(linger);749}750751@Override752protected UdpEchoRequest createConnection(DatagramSocket socket,753DatagramPacket request) {754return new UdpEchoRequest(socket, request);755}756757private final class UdpEchoRequest extends UdpRequestThread {758759public UdpEchoRequest(DatagramSocket socket, DatagramPacket request) {760super(socket, request);761}762763@Override764public void run() {765try {766lingerIfRequired();767final DatagramPacket response =768new DatagramPacket(request.getData(),769request.getOffset(), request.getLength(),770request.getAddress(), request.getPort());771send(socket, response);772} catch (IOException io) {773System.err.println("Failed to send response: " + io);774io.printStackTrace(System.err);775}776}777}778779public static UdpEchoServer startNewServer() throws IOException {780return startNewServer(0);781}782783public static UdpEchoServer startNewServer(long linger) throws IOException {784final UdpEchoServer echoServer = new UdpEchoServer(linger);785echoServer.start();786return echoServer;787}788}789790/**791* A small UDP server that emulates the Day & Time service for tests792* purposes. See http://en.wikipedia.org/wiki/Daytime_Protocol This server793* uses an anonymous port - NOT the standard port 13. We don't guarantee794* that its behavior exactly matches the RFC - the only purpose of this795* server is to have something that responds to nio tests...796*/797static final class UdpDayTimeServer extends AbstractUdpServer {798799public UdpDayTimeServer() {800this(0L);801}802803public UdpDayTimeServer(long linger) {804super(linger);805}806807@Override808protected UdpDayTimeRequestThread createConnection(DatagramSocket socket,809DatagramPacket request) {810return new UdpDayTimeRequestThread(socket, request);811}812813private final class UdpDayTimeRequestThread extends UdpRequestThread {814815public UdpDayTimeRequestThread(DatagramSocket socket,816DatagramPacket request) {817super(socket, request);818}819820@Override821public void run() {822try {823lingerIfRequired();824final byte[] data = new Date(System.currentTimeMillis())825.toString().getBytes("US-ASCII");826final DatagramPacket response =827new DatagramPacket(data, 0, data.length,828request.getAddress(), request.getPort());829send(socket, response);830} catch (IOException io) {831System.err.println("Failed to send response: " + io);832io.printStackTrace(System.err);833}834}835}836837public static UdpDayTimeServer startNewServer() throws IOException {838return startNewServer(0);839}840841public static UdpDayTimeServer startNewServer(long linger)842throws IOException {843final UdpDayTimeServer echoServer = new UdpDayTimeServer(linger);844echoServer.start();845return echoServer;846}847}848}849850851