Path: blob/master/test/jdk/java/nio/channels/TestServers.java
41149 views
/*1* Copyright (c) 2012, 2018, 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;3233import jdk.test.lib.Utils;343536public class TestServers {3738private TestServers() { }3940/**41* An abstract server identifies a server which listens on a port on on a42* given machine.43*/44static abstract class AbstractServer {4546private AbstractServer() {47}4849public abstract int getPort();5051public abstract InetAddress getAddress();52}5354/**55* A downgraded type of AbstractServer which will refuse connections. Note:56* use it once and throw it away - this implementation opens an anonymous57* socket and closes it, returning the address of the closed socket. If58* other servers are started afterwards, the address/port might get reused59* and become connectable again - so it's not a good idea to assume that60* connections using this address/port will always be refused. Connections61* will be refused as long as the address/port of the refusing server has62* not been reused.63*/64static class RefusingServer extends AbstractServer {6566final InetAddress address;67final int port;6869private RefusingServer(InetAddress address, int port) {70this.address = address;71this.port = port;72}7374@Override75public int getPort() {76return port;77}7879@Override80public InetAddress getAddress() {81return address;82}8384public static RefusingServer newRefusingServer() throws IOException {85return new RefusingServer(InetAddress.getLocalHost(),86Utils.refusingEndpoint().getPort());87}88}8990/**91* An abstract class for implementing small TCP servers for the nio tests92* purposes. Disclaimer: This is a naive implementation that uses the old93* networking APIs (not those from {@code java.nio.*}) and shamelessly94* extends/creates Threads instead of using an executor service.95*/96static abstract class AbstractTcpServer extends AbstractServer97implements Runnable, Closeable {9899protected final long linger; // #of ms to wait before responding100private Thread acceptThread; // thread waiting for accept101// list of opened connections that should be closed on close.102private List<TcpConnectionThread> connections = new ArrayList<>();103private ServerSocket serverSocket; // the server socket104private boolean started = false; // whether the server is started105Throwable error = null;106107/**108* Creates a new abstract TCP server.109*110* @param linger the amount of time the server should wait before111* responding to requests.112*/113protected AbstractTcpServer(long linger) {114this.linger = linger;115}116117/**118* The local port to which the server is bound.119*120* @return The local port to which the server is bound.121* @exception IllegalStateException is thrown if the server is not122* started.123*/124@Override125public final synchronized int getPort() {126if (!started) {127throw new IllegalStateException("Not started");128}129return serverSocket.getLocalPort();130}131132/**133* The local address to which the server is bound.134*135* @return The local address to which the server is bound.136* @exception IllegalStateException is thrown if the server is not137* started.138*/139@Override140public final synchronized InetAddress getAddress() {141if (!started) {142throw new IllegalStateException("Not started");143}144return serverSocket.getInetAddress();145}146147/**148* Tells whether the server is started.149*150* @return true if the server is started.151*/152public final synchronized boolean isStarted() {153return started;154}155156/**157* Creates a new server socket.158*159* @param port local port to bind to.160* @param backlog requested maximum length of the queue of incoming161* connections.162* @param address local address to bind to.163* @return a new bound server socket ready to accept connections.164* @throws IOException if the socket cannot be created or bound.165*/166protected ServerSocket newServerSocket(int port, int backlog,167InetAddress address)168throws IOException {169return new ServerSocket(port, backlog, address);170}171172/**173* Starts listening for connections.174*175* @throws IOException if the server socket cannot be created or bound.176*/177public final synchronized void start() throws IOException {178if (started) {179return;180}181final ServerSocket socket =182newServerSocket(0, 100, InetAddress.getLocalHost());183serverSocket = socket;184acceptThread = new Thread(this);185acceptThread.setDaemon(true);186acceptThread.start();187started = true;188}189190/**191* Calls {@code Thread.sleep(linger);}192*/193protected final void lingerIfRequired() {194if (linger > 0) {195try {196Thread.sleep(linger);197} catch (InterruptedException x) {198Thread.interrupted();199final ServerSocket socket = serverSocket();200if (socket != null && !socket.isClosed()) {201System.err.println("Thread interrupted...");202}203}204}205}206207final synchronized ServerSocket serverSocket() {208return this.serverSocket;209}210211/**212* The main accept loop.213*/214@Override215public final void run() {216final ServerSocket sSocket = serverSocket();217try {218Socket s;219while (isStarted() && !Thread.interrupted()220&& (s = sSocket.accept()) != null) {221lingerIfRequired();222listen(s);223}224} catch (Exception x) {225error = x;226} finally {227synchronized (this) {228if (!sSocket.isClosed()) {229try {230sSocket.close();231} catch (IOException x) {232System.err.println("Failed to close server socket");233}234}235if (started && this.serverSocket == sSocket) {236started = false;237this.serverSocket = null;238this.acceptThread = null;239}240}241}242}243244/**245* Represents a connection accepted by the server.246*/247protected abstract class TcpConnectionThread extends Thread {248249protected final Socket socket;250251protected TcpConnectionThread(Socket socket) {252this.socket = socket;253this.setDaemon(true);254}255256public void close() throws IOException {257socket.close();258interrupt();259}260}261262/**263* Creates a new TcpConnnectionThread to handle the connection through264* an accepted socket.265*266* @param s the socket returned by {@code serverSocket.accept()}.267* @return a new TcpConnnectionThread to handle the connection through268* an accepted socket.269*/270protected abstract TcpConnectionThread createConnection(Socket s);271272/**273* Creates and starts a new TcpConnectionThread to handle the accepted274* socket.275*276* @param s the socket returned by {@code serverSocket.accept()}.277*/278private synchronized void listen(Socket s) {279TcpConnectionThread c = createConnection(s);280c.start();281addConnection(c);282}283284/**285* Add the connection to the list of accepted connections.286*287* @param connection an accepted connection.288*/289protected synchronized void addConnection(290TcpConnectionThread connection) {291connections.add(connection);292}293294/**295* Remove the connection from the list of accepted connections.296*297* @param connection an accepted connection.298*/299protected synchronized void removeConnection(300TcpConnectionThread connection) {301connections.remove(connection);302}303304/**305* Close the server socket and all the connections present in the list306* of accepted connections.307*308* @throws IOException309*/310@Override311public synchronized void close() throws IOException {312if (serverSocket != null && !serverSocket.isClosed()) {313serverSocket.close();314}315if (acceptThread != null) {316acceptThread.interrupt();317}318int failed = 0;319for (TcpConnectionThread c : connections) {320try {321c.close();322} catch (IOException x) {323// no matter - we're closing.324failed++;325}326}327connections.clear();328if (failed > 0) {329throw new IOException("Failed to close some connections");330}331}332}333334/**335* A small TCP Server that emulates the echo service for tests purposes. See336* http://en.wikipedia.org/wiki/Echo_Protocol This server uses an anonymous337* port - NOT the standard port 7. We don't guarantee that its behavior338* exactly matches the RFC - the only purpose of this server is to have339* something that responds to nio tests...340*/341static class EchoServer extends AbstractTcpServer {342343public EchoServer() {344this(0L);345}346347public EchoServer(long linger) {348super(linger);349}350351@Override352protected TcpConnectionThread createConnection(Socket s) {353return new EchoConnection(s);354}355356private final class EchoConnection extends TcpConnectionThread {357358public EchoConnection(Socket socket) {359super(socket);360}361362@Override363public void run() {364try {365final InputStream is = socket.getInputStream();366final OutputStream out = socket.getOutputStream();367byte[] b = new byte[255];368int n;369while ((n = is.read(b)) > 0) {370lingerIfRequired();371out.write(b, 0, n);372}373} catch (IOException io) {374// fall through to finally375} finally {376if (!socket.isClosed()) {377try {378socket.close();379} catch (IOException x) {380System.err.println(381"Failed to close echo connection socket");382}383}384removeConnection(this);385}386}387}388389public static EchoServer startNewServer() throws IOException {390return startNewServer(0);391}392393public static EchoServer startNewServer(long linger) throws IOException {394final EchoServer echoServer = new EchoServer(linger);395echoServer.start();396return echoServer;397}398}399400/**401* A small TCP Server that accept connections but does not response to any input.402*/403static final class NoResponseServer extends EchoServer {404public NoResponseServer() {405super(Long.MAX_VALUE);406}407408public static NoResponseServer startNewServer() throws IOException {409final NoResponseServer noResponseServer = new NoResponseServer();410noResponseServer.start();411return noResponseServer;412}413}414415/**416* A small TCP server that emulates the Day & Time service for tests417* purposes. See http://en.wikipedia.org/wiki/Daytime_Protocol This server418* uses an anonymous port - NOT the standard port 13. We don't guarantee419* that its behavior exactly matches the RFC - the only purpose of this420* server is to have something that responds to nio tests...421*/422static final class DayTimeServer extends AbstractTcpServer {423424public DayTimeServer() {425this(0L);426}427428public DayTimeServer(long linger) {429super(linger);430}431432@Override433protected TcpConnectionThread createConnection(Socket s) {434return new DayTimeServerConnection(s);435}436437@Override438protected void addConnection(TcpConnectionThread connection) {439// do nothing - the connection just write the date and terminates.440}441442@Override443protected void removeConnection(TcpConnectionThread connection) {444// do nothing - we're not adding connections to the list...445}446447private final class DayTimeServerConnection extends TcpConnectionThread {448449public DayTimeServerConnection(Socket socket) {450super(socket);451}452453@Override454public void run() {455try {456final OutputStream out = socket.getOutputStream();457lingerIfRequired();458out.write(new Date(System.currentTimeMillis())459.toString().getBytes("US-ASCII"));460out.flush();461} catch (IOException io) {462// fall through to finally463} finally {464if (!socket.isClosed()) {465try {466socket.close();467} catch (IOException x) {468System.err.println(469"Failed to close echo connection socket");470}471}472}473}474}475476public static DayTimeServer startNewServer()477throws IOException {478return startNewServer(0);479}480481public static DayTimeServer startNewServer(long linger)482throws IOException {483final DayTimeServer daytimeServer = new DayTimeServer(linger);484daytimeServer.start();485return daytimeServer;486}487}488489/**490* An abstract class for implementing small UDP Servers for the nio tests491* purposes. Disclaimer: This is a naive implementation that uses the old492* networking APIs (not those from {@code java.nio.*}) and shamelessly493* extends/creates Threads instead of using an executor service.494*/495static abstract class AbstractUdpServer extends AbstractServer496implements Runnable, Closeable {497498protected final long linger; // #of ms to wait before responding499private Thread acceptThread; // thread waiting for packets500private DatagramSocket serverSocket; // the server socket501private boolean started = false; // whether the server is started502Throwable error = null;503504/**505* Creates a new abstract UDP server.506*507* @param linger the amount of time the server should wait before508* responding to requests.509*/510protected AbstractUdpServer(long linger) {511this.linger = linger;512}513514/**515* The local port to which the server is bound.516*517* @return The local port to which the server is bound.518* @exception IllegalStateException is thrown if the server is not519* started.520*/521@Override522public final synchronized int getPort() {523if (!started) {524throw new IllegalStateException("Not started");525}526return serverSocket.getLocalPort();527}528529/**530* The local address to which the server is bound.531*532* @return The local address to which the server is bound.533* @exception IllegalStateException is thrown if the server is not534* started.535*/536@Override537public final synchronized InetAddress getAddress() {538if (!started) {539throw new IllegalStateException("Not started");540}541return serverSocket.getLocalAddress();542}543544/**545* Tells whether the server is started.546*547* @return true if the server is started.548*/549public final synchronized boolean isStarted() {550return started;551}552553/**554* Creates a new datagram socket.555*556* @param port local port to bind to.557* @param address local address to bind to.558* @return a new bound server socket ready to listen for packets.559* @throws IOException if the socket cannot be created or bound.560*/561protected DatagramSocket newDatagramSocket(int port,562InetAddress address)563throws IOException {564return new DatagramSocket(port, address);565}566567/**568* Starts listening for connections.569*570* @throws IOException if the server socket cannot be created or bound.571*/572public final synchronized void start() throws IOException {573if (started) {574return;575}576final DatagramSocket socket =577newDatagramSocket(0, InetAddress.getLocalHost());578serverSocket = socket;579acceptThread = new Thread(this);580acceptThread.setDaemon(true);581acceptThread.start();582started = true;583}584585/**586* Calls {@code Thread.sleep(linger);}587*/588protected final void lingerIfRequired() {589if (linger > 0) {590try {591Thread.sleep(linger);592} catch (InterruptedException x) {593Thread.interrupted();594final DatagramSocket socket = serverSocket();595if (socket != null && !socket.isClosed()) {596System.err.println("Thread interrupted...");597}598}599}600}601602final synchronized DatagramSocket serverSocket() {603return this.serverSocket;604}605606final synchronized boolean send(DatagramSocket socket,607DatagramPacket response) throws IOException {608if (!socket.isClosed()) {609socket.send(response);610return true;611} else {612return false;613}614}615616/**617* The main receive loop.618*/619@Override620public final void run() {621final DatagramSocket sSocket = serverSocket();622try {623final int size = Math.max(1024, sSocket.getReceiveBufferSize());624if (size > sSocket.getReceiveBufferSize()) {625sSocket.setReceiveBufferSize(size);626}627while (isStarted() && !Thread.interrupted() && !sSocket.isClosed()) {628final byte[] buf = new byte[size];629final DatagramPacket packet =630new DatagramPacket(buf, buf.length);631lingerIfRequired();632sSocket.receive(packet);633//System.out.println("Received packet from: "634// + packet.getAddress()+":"+packet.getPort());635handle(sSocket, packet);636}637} catch (Exception x) {638error = x;639} finally {640synchronized (this) {641if (!sSocket.isClosed()) {642sSocket.close();643}644if (started && this.serverSocket == sSocket) {645started = false;646this.serverSocket = null;647this.acceptThread = null;648}649}650}651}652653/**654* Represents an UDP request received by the server.655*/656protected abstract class UdpRequestThread extends Thread {657658protected final DatagramPacket request;659protected final DatagramSocket socket;660661protected UdpRequestThread(DatagramSocket socket, DatagramPacket request) {662this.socket = socket;663this.request = request;664this.setDaemon(true);665}666}667668/**669* Creates a new UdpRequestThread to handle a DatagramPacket received670* through a DatagramSocket.671*672* @param socket the socket through which the request was received.673* @param request the datagram packet received through the socket.674* @return a new UdpRequestThread to handle the request received through675* a DatagramSocket.676*/677protected abstract UdpRequestThread createConnection(DatagramSocket socket,678DatagramPacket request);679680/**681* Creates and starts a new UdpRequestThread to handle the received682* datagram packet.683*684* @param socket the socket through which the request was received.685* @param request the datagram packet received through the socket.686*/687private synchronized void handle(DatagramSocket socket,688DatagramPacket request) {689UdpRequestThread c = createConnection(socket, request);690// c can be null if the request requires no response.691if (c != null) {692c.start();693}694}695696/**697* Close the server socket.698*699* @throws IOException700*/701@Override702public synchronized void close() throws IOException {703if (serverSocket != null && !serverSocket.isClosed()) {704serverSocket.close();705}706if (acceptThread != null) {707acceptThread.interrupt();708}709}710}711712/**713* A small UDP Server that emulates the discard service for tests purposes.714* See http://en.wikipedia.org/wiki/Discard_Protocol This server uses an715* anonymous port - NOT the standard port 9. We don't guarantee that its716* behavior exactly matches the RFC - the only purpose of this server is to717* have something that responds to nio tests...718*/719static final class UdpDiscardServer extends AbstractUdpServer {720721public UdpDiscardServer() {722this(0L);723}724725public UdpDiscardServer(long linger) {726super(linger);727}728729@Override730protected UdpRequestThread createConnection(DatagramSocket socket,731DatagramPacket request) {732// no response required733return null;734}735736public static UdpDiscardServer startNewServer() throws IOException {737return startNewServer(0);738}739740public static UdpDiscardServer startNewServer(long linger) throws IOException {741final UdpDiscardServer discardServer = new UdpDiscardServer(linger);742discardServer.start();743return discardServer;744}745}746747/**748* A small UDP Server that emulates the echo service for tests purposes. See749* http://en.wikipedia.org/wiki/Echo_Protocol This server uses an anonymous750* port - NOT the standard port 7. We don't guarantee that its behavior751* exactly matches the RFC - the only purpose of this server is to have752* something that responds to nio tests...753*/754static final class UdpEchoServer extends AbstractUdpServer {755756public UdpEchoServer() {757this(0L);758}759760public UdpEchoServer(long linger) {761super(linger);762}763764@Override765protected UdpEchoRequest createConnection(DatagramSocket socket,766DatagramPacket request) {767return new UdpEchoRequest(socket, request);768}769770private final class UdpEchoRequest extends UdpRequestThread {771772public UdpEchoRequest(DatagramSocket socket, DatagramPacket request) {773super(socket, request);774}775776@Override777public void run() {778try {779lingerIfRequired();780final DatagramPacket response =781new DatagramPacket(request.getData(),782request.getOffset(), request.getLength(),783request.getAddress(), request.getPort());784send(socket, response);785} catch (IOException io) {786System.err.println("Failed to send response: " + io);787io.printStackTrace(System.err);788}789}790}791792public static UdpEchoServer startNewServer() throws IOException {793return startNewServer(0);794}795796public static UdpEchoServer startNewServer(long linger) throws IOException {797final UdpEchoServer echoServer = new UdpEchoServer(linger);798echoServer.start();799return echoServer;800}801}802803/**804* A small UDP server that emulates the Day & Time service for tests805* purposes. See http://en.wikipedia.org/wiki/Daytime_Protocol This server806* uses an anonymous port - NOT the standard port 13. We don't guarantee807* that its behavior exactly matches the RFC - the only purpose of this808* server is to have something that responds to nio tests...809*/810static final class UdpDayTimeServer extends AbstractUdpServer {811812public UdpDayTimeServer() {813this(0L);814}815816public UdpDayTimeServer(long linger) {817super(linger);818}819820@Override821protected UdpDayTimeRequestThread createConnection(DatagramSocket socket,822DatagramPacket request) {823return new UdpDayTimeRequestThread(socket, request);824}825826private final class UdpDayTimeRequestThread extends UdpRequestThread {827828public UdpDayTimeRequestThread(DatagramSocket socket,829DatagramPacket request) {830super(socket, request);831}832833@Override834public void run() {835try {836lingerIfRequired();837final byte[] data = new Date(System.currentTimeMillis())838.toString().getBytes("US-ASCII");839final DatagramPacket response =840new DatagramPacket(data, 0, data.length,841request.getAddress(), request.getPort());842send(socket, response);843} catch (IOException io) {844System.err.println("Failed to send response: " + io);845io.printStackTrace(System.err);846}847}848}849850public static UdpDayTimeServer startNewServer() throws IOException {851return startNewServer(0);852}853854public static UdpDayTimeServer startNewServer(long linger)855throws IOException {856final UdpDayTimeServer echoServer = new UdpDayTimeServer(linger);857echoServer.start();858return echoServer;859}860}861}862863864