undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
885
test/jdk/java/nio/channels/TestServers.java
Normal file
885
test/jdk/java/nio/channels/TestServers.java
Normal file
|
|
@ -0,0 +1,885 @@
|
|||
/*
|
||||
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/* Test utility classes
|
||||
*
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
|
||||
public class TestServers {
|
||||
|
||||
private TestServers() { }
|
||||
|
||||
/**
|
||||
* An abstract server identifies a server which listens on a port on a
|
||||
* given machine.
|
||||
*/
|
||||
static abstract class AbstractServer {
|
||||
|
||||
private AbstractServer() {
|
||||
}
|
||||
|
||||
public abstract int getPort();
|
||||
|
||||
public abstract InetAddress getAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* A downgraded type of AbstractServer which will refuse connections. Note:
|
||||
* use it once and throw it away - this implementation opens an anonymous
|
||||
* socket and closes it, returning the address of the closed socket. If
|
||||
* other servers are started afterwards, the address/port might get reused
|
||||
* and become connectable again - so it's not a good idea to assume that
|
||||
* connections using this address/port will always be refused. Connections
|
||||
* will be refused as long as the address/port of the refusing server has
|
||||
* not been reused.
|
||||
*/
|
||||
static class RefusingServer extends AbstractServer {
|
||||
|
||||
final InetAddress address;
|
||||
final int port;
|
||||
|
||||
private RefusingServer(InetAddress address, int port) {
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetAddress getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public static RefusingServer newRefusingServer() throws IOException {
|
||||
return new RefusingServer(InetAddress.getLocalHost(),
|
||||
Utils.refusingEndpoint().getPort());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An abstract class for implementing small TCP servers for the nio tests
|
||||
* purposes. Disclaimer: This is a naive implementation that uses the old
|
||||
* networking APIs (not those from {@code java.nio.*}) and shamelessly
|
||||
* extends/creates Threads instead of using an executor service.
|
||||
*/
|
||||
static abstract class AbstractTcpServer extends AbstractServer
|
||||
implements Runnable, Closeable {
|
||||
|
||||
protected final long linger; // #of ms to wait before responding
|
||||
private Thread acceptThread; // thread waiting for accept
|
||||
// list of opened connections that should be closed on close.
|
||||
private List<TcpConnectionThread> connections = new ArrayList<>();
|
||||
private ServerSocket serverSocket; // the server socket
|
||||
private boolean started = false; // whether the server is started
|
||||
Throwable error = null;
|
||||
|
||||
/**
|
||||
* Creates a new abstract TCP server.
|
||||
*
|
||||
* @param linger the amount of time the server should wait before
|
||||
* responding to requests.
|
||||
*/
|
||||
protected AbstractTcpServer(long linger) {
|
||||
this.linger = linger;
|
||||
}
|
||||
|
||||
/**
|
||||
* The local port to which the server is bound.
|
||||
*
|
||||
* @return The local port to which the server is bound.
|
||||
* @exception IllegalStateException is thrown if the server is not
|
||||
* started.
|
||||
*/
|
||||
@Override
|
||||
public final synchronized int getPort() {
|
||||
if (!started) {
|
||||
throw new IllegalStateException("Not started");
|
||||
}
|
||||
return serverSocket.getLocalPort();
|
||||
}
|
||||
|
||||
/**
|
||||
* The local address to which the server is bound.
|
||||
*
|
||||
* @return The local address to which the server is bound.
|
||||
* @exception IllegalStateException is thrown if the server is not
|
||||
* started.
|
||||
*/
|
||||
@Override
|
||||
public final synchronized InetAddress getAddress() {
|
||||
if (!started) {
|
||||
throw new IllegalStateException("Not started");
|
||||
}
|
||||
return serverSocket.getInetAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether the server is started.
|
||||
*
|
||||
* @return true if the server is started.
|
||||
*/
|
||||
public final synchronized boolean isStarted() {
|
||||
return started;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new server socket.
|
||||
*
|
||||
* @param port local port to bind to.
|
||||
* @param backlog requested maximum length of the queue of incoming
|
||||
* connections.
|
||||
* @param address local address to bind to.
|
||||
* @return a new bound server socket ready to accept connections.
|
||||
* @throws IOException if the socket cannot be created or bound.
|
||||
*/
|
||||
protected ServerSocket newServerSocket(int port, int backlog,
|
||||
InetAddress address)
|
||||
throws IOException {
|
||||
return new ServerSocket(port, backlog, address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts listening for connections.
|
||||
*
|
||||
* @throws IOException if the server socket cannot be created or bound.
|
||||
*/
|
||||
public final synchronized void start() throws IOException {
|
||||
if (started) {
|
||||
return;
|
||||
}
|
||||
final ServerSocket socket =
|
||||
newServerSocket(0, 100, InetAddress.getLocalHost());
|
||||
serverSocket = socket;
|
||||
acceptThread = new Thread(this);
|
||||
acceptThread.setDaemon(true);
|
||||
acceptThread.start();
|
||||
started = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@code Thread.sleep(linger);}
|
||||
*/
|
||||
protected final void lingerIfRequired() {
|
||||
if (linger > 0) {
|
||||
try {
|
||||
Thread.sleep(linger);
|
||||
} catch (InterruptedException x) {
|
||||
Thread.interrupted();
|
||||
final ServerSocket socket = serverSocket();
|
||||
if (socket != null && !socket.isClosed()) {
|
||||
System.err.println("Thread interrupted...");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final synchronized ServerSocket serverSocket() {
|
||||
return this.serverSocket;
|
||||
}
|
||||
|
||||
/**
|
||||
* The main accept loop.
|
||||
*/
|
||||
@Override
|
||||
public final void run() {
|
||||
final ServerSocket sSocket = serverSocket();
|
||||
try {
|
||||
Socket s;
|
||||
while (isStarted() && !Thread.interrupted()
|
||||
&& (s = sSocket.accept()) != null) {
|
||||
lingerIfRequired();
|
||||
listen(s);
|
||||
}
|
||||
} catch (Exception x) {
|
||||
error = x;
|
||||
} finally {
|
||||
synchronized (this) {
|
||||
if (!sSocket.isClosed()) {
|
||||
try {
|
||||
sSocket.close();
|
||||
} catch (IOException x) {
|
||||
System.err.println("Failed to close server socket");
|
||||
}
|
||||
}
|
||||
if (started && this.serverSocket == sSocket) {
|
||||
started = false;
|
||||
this.serverSocket = null;
|
||||
this.acceptThread = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a connection accepted by the server.
|
||||
*/
|
||||
protected abstract class TcpConnectionThread extends Thread {
|
||||
|
||||
protected final Socket socket;
|
||||
|
||||
protected TcpConnectionThread(Socket socket) {
|
||||
this.socket = socket;
|
||||
this.setDaemon(true);
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
socket.close();
|
||||
interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new TcpConnnectionThread to handle the connection through
|
||||
* an accepted socket.
|
||||
*
|
||||
* @param s the socket returned by {@code serverSocket.accept()}.
|
||||
* @return a new TcpConnnectionThread to handle the connection through
|
||||
* an accepted socket.
|
||||
*/
|
||||
protected abstract TcpConnectionThread createConnection(Socket s);
|
||||
|
||||
/**
|
||||
* Creates and starts a new TcpConnectionThread to handle the accepted
|
||||
* socket.
|
||||
*
|
||||
* @param s the socket returned by {@code serverSocket.accept()}.
|
||||
*/
|
||||
private synchronized void listen(Socket s) {
|
||||
TcpConnectionThread c = createConnection(s);
|
||||
c.start();
|
||||
addConnection(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the connection to the list of accepted connections.
|
||||
*
|
||||
* @param connection an accepted connection.
|
||||
*/
|
||||
protected synchronized void addConnection(
|
||||
TcpConnectionThread connection) {
|
||||
connections.add(connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the connection from the list of accepted connections.
|
||||
*
|
||||
* @param connection an accepted connection.
|
||||
*/
|
||||
protected synchronized void removeConnection(
|
||||
TcpConnectionThread connection) {
|
||||
connections.remove(connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the server socket and all the connections present in the list
|
||||
* of accepted connections.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public synchronized void close() throws IOException {
|
||||
if (serverSocket != null && !serverSocket.isClosed()) {
|
||||
serverSocket.close();
|
||||
}
|
||||
if (acceptThread != null) {
|
||||
acceptThread.interrupt();
|
||||
}
|
||||
int failed = 0;
|
||||
for (TcpConnectionThread c : connections) {
|
||||
try {
|
||||
c.close();
|
||||
} catch (IOException x) {
|
||||
// no matter - we're closing.
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
connections.clear();
|
||||
if (failed > 0) {
|
||||
throw new IOException("Failed to close some connections");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A small TCP Server that emulates the echo service for tests purposes. See
|
||||
* http://en.wikipedia.org/wiki/Echo_Protocol This server uses an anonymous
|
||||
* port - NOT the standard port 7. We don't guarantee that its behavior
|
||||
* exactly matches the RFC - the only purpose of this server is to have
|
||||
* something that responds to nio tests...
|
||||
*/
|
||||
static class EchoServer extends AbstractTcpServer {
|
||||
|
||||
public EchoServer() {
|
||||
this(0L);
|
||||
}
|
||||
|
||||
public EchoServer(long linger) {
|
||||
super(linger);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TcpConnectionThread createConnection(Socket s) {
|
||||
return new EchoConnection(s);
|
||||
}
|
||||
|
||||
private final class EchoConnection extends TcpConnectionThread {
|
||||
|
||||
public EchoConnection(Socket socket) {
|
||||
super(socket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
final InputStream is = socket.getInputStream();
|
||||
final OutputStream out = socket.getOutputStream();
|
||||
byte[] b = new byte[255];
|
||||
int n;
|
||||
while ((n = is.read(b)) > 0) {
|
||||
lingerIfRequired();
|
||||
out.write(b, 0, n);
|
||||
}
|
||||
} catch (IOException io) {
|
||||
// fall through to finally
|
||||
} finally {
|
||||
if (!socket.isClosed()) {
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException x) {
|
||||
System.err.println(
|
||||
"Failed to close echo connection socket");
|
||||
}
|
||||
}
|
||||
removeConnection(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static EchoServer startNewServer() throws IOException {
|
||||
return startNewServer(0);
|
||||
}
|
||||
|
||||
public static EchoServer startNewServer(long linger) throws IOException {
|
||||
final EchoServer echoServer = new EchoServer(linger);
|
||||
echoServer.start();
|
||||
return echoServer;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A small TCP Server that accept connections but does not response to any input.
|
||||
*/
|
||||
static final class NoResponseServer extends EchoServer {
|
||||
public NoResponseServer() {
|
||||
super(Long.MAX_VALUE);
|
||||
}
|
||||
|
||||
public static NoResponseServer startNewServer() throws IOException {
|
||||
final NoResponseServer noResponseServer = new NoResponseServer();
|
||||
noResponseServer.start();
|
||||
return noResponseServer;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A small TCP server that emulates the Day & Time service for tests
|
||||
* purposes. See http://en.wikipedia.org/wiki/Daytime_Protocol This server
|
||||
* uses an anonymous port - NOT the standard port 13. We don't guarantee
|
||||
* that its behavior exactly matches the RFC - the only purpose of this
|
||||
* server is to have something that responds to nio tests...
|
||||
*/
|
||||
static final class DayTimeServer extends AbstractTcpServer {
|
||||
|
||||
public DayTimeServer() {
|
||||
this(0L);
|
||||
}
|
||||
|
||||
public DayTimeServer(long linger) {
|
||||
super(linger);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TcpConnectionThread createConnection(Socket s) {
|
||||
return new DayTimeServerConnection(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addConnection(TcpConnectionThread connection) {
|
||||
// do nothing - the connection just write the date and terminates.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeConnection(TcpConnectionThread connection) {
|
||||
// do nothing - we're not adding connections to the list...
|
||||
}
|
||||
|
||||
private final class DayTimeServerConnection extends TcpConnectionThread {
|
||||
|
||||
public DayTimeServerConnection(Socket socket) {
|
||||
super(socket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
final OutputStream out = socket.getOutputStream();
|
||||
lingerIfRequired();
|
||||
out.write(new Date(System.currentTimeMillis())
|
||||
.toString().getBytes("US-ASCII"));
|
||||
out.flush();
|
||||
} catch (IOException io) {
|
||||
// fall through to finally
|
||||
} finally {
|
||||
if (!socket.isClosed()) {
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException x) {
|
||||
System.err.println(
|
||||
"Failed to close echo connection socket");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static DayTimeServer startNewServer()
|
||||
throws IOException {
|
||||
return startNewServer(0);
|
||||
}
|
||||
|
||||
public static DayTimeServer startNewServer(long linger)
|
||||
throws IOException {
|
||||
final DayTimeServer daytimeServer = new DayTimeServer(linger);
|
||||
daytimeServer.start();
|
||||
return daytimeServer;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An abstract class for implementing small UDP Servers for the nio tests
|
||||
* purposes. Disclaimer: This is a naive implementation that uses the old
|
||||
* networking APIs (not those from {@code java.nio.*}) and shamelessly
|
||||
* extends/creates Threads instead of using an executor service.
|
||||
*/
|
||||
static abstract class AbstractUdpServer extends AbstractServer
|
||||
implements Runnable, Closeable {
|
||||
|
||||
protected final long linger; // #of ms to wait before responding
|
||||
protected final InetAddress bindAddress; //local address to bind to; can be null.
|
||||
private Thread acceptThread; // thread waiting for packets
|
||||
private DatagramSocket serverSocket; // the server socket
|
||||
private boolean started = false; // whether the server is started
|
||||
Throwable error = null;
|
||||
|
||||
/**
|
||||
* Creates a new abstract UDP server.
|
||||
*
|
||||
* @param linger the amount of time the server should wait before
|
||||
* responding to requests.
|
||||
*/
|
||||
protected AbstractUdpServer(long linger) {
|
||||
this(linger, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new abstract UDP server.
|
||||
*
|
||||
* @param linger the amount of time the server should wait before
|
||||
* responding to requests.
|
||||
* @param bindAddress the address to bind to. If {@code null}, will
|
||||
* bind to InetAddress.getLocalHost();
|
||||
*/
|
||||
protected AbstractUdpServer(long linger, InetAddress bindAddress) {
|
||||
this.linger = linger;
|
||||
this.bindAddress = bindAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* The local port to which the server is bound.
|
||||
*
|
||||
* @return The local port to which the server is bound.
|
||||
* @exception IllegalStateException is thrown if the server is not
|
||||
* started.
|
||||
*/
|
||||
@Override
|
||||
public final synchronized int getPort() {
|
||||
if (!started) {
|
||||
throw new IllegalStateException("Not started");
|
||||
}
|
||||
return serverSocket.getLocalPort();
|
||||
}
|
||||
|
||||
/**
|
||||
* The local address to which the server is bound.
|
||||
*
|
||||
* @return The local address to which the server is bound.
|
||||
* @exception IllegalStateException is thrown if the server is not
|
||||
* started.
|
||||
*/
|
||||
@Override
|
||||
public final synchronized InetAddress getAddress() {
|
||||
if (!started) {
|
||||
throw new IllegalStateException("Not started");
|
||||
}
|
||||
return serverSocket.getLocalAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether the server is started.
|
||||
*
|
||||
* @return true if the server is started.
|
||||
*/
|
||||
public final synchronized boolean isStarted() {
|
||||
return started;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new datagram socket.
|
||||
*
|
||||
* @param port local port to bind to.
|
||||
* @param address local address to bind to.
|
||||
* @return a new bound server socket ready to listen for packets.
|
||||
* @throws IOException if the socket cannot be created or bound.
|
||||
*/
|
||||
protected DatagramSocket newDatagramSocket(int port,
|
||||
InetAddress address)
|
||||
throws IOException {
|
||||
return new DatagramSocket(port, address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts listening for connections.
|
||||
*
|
||||
* @throws IOException if the server socket cannot be created or bound.
|
||||
*/
|
||||
public final synchronized void start() throws IOException {
|
||||
if (started) {
|
||||
return;
|
||||
}
|
||||
InetAddress lh = bindAddress == null ? InetAddress.getLocalHost() : bindAddress;
|
||||
final DatagramSocket socket =
|
||||
newDatagramSocket(0, lh);
|
||||
serverSocket = socket;
|
||||
acceptThread = new Thread(this);
|
||||
acceptThread.setDaemon(true);
|
||||
acceptThread.start();
|
||||
started = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@code Thread.sleep(linger);}
|
||||
*/
|
||||
protected final void lingerIfRequired() {
|
||||
if (linger > 0) {
|
||||
try {
|
||||
Thread.sleep(linger);
|
||||
} catch (InterruptedException x) {
|
||||
Thread.interrupted();
|
||||
final DatagramSocket socket = serverSocket();
|
||||
if (socket != null && !socket.isClosed()) {
|
||||
System.err.println("Thread interrupted...");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final synchronized DatagramSocket serverSocket() {
|
||||
return this.serverSocket;
|
||||
}
|
||||
|
||||
final synchronized boolean send(DatagramSocket socket,
|
||||
DatagramPacket response) throws IOException {
|
||||
if (!socket.isClosed()) {
|
||||
socket.send(response);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The main receive loop.
|
||||
*/
|
||||
@Override
|
||||
public final void run() {
|
||||
final DatagramSocket sSocket = serverSocket();
|
||||
try {
|
||||
final int size = Math.max(1024, sSocket.getReceiveBufferSize());
|
||||
if (size > sSocket.getReceiveBufferSize()) {
|
||||
sSocket.setReceiveBufferSize(size);
|
||||
}
|
||||
while (isStarted() && !Thread.interrupted() && !sSocket.isClosed()) {
|
||||
final byte[] buf = new byte[size];
|
||||
final DatagramPacket packet =
|
||||
new DatagramPacket(buf, buf.length);
|
||||
lingerIfRequired();
|
||||
sSocket.receive(packet);
|
||||
//System.out.println("Received packet from: "
|
||||
// + packet.getAddress()+":"+packet.getPort());
|
||||
handle(sSocket, packet);
|
||||
}
|
||||
} catch (Exception x) {
|
||||
error = x;
|
||||
} finally {
|
||||
synchronized (this) {
|
||||
if (!sSocket.isClosed()) {
|
||||
sSocket.close();
|
||||
}
|
||||
if (started && this.serverSocket == sSocket) {
|
||||
started = false;
|
||||
this.serverSocket = null;
|
||||
this.acceptThread = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an UDP request received by the server.
|
||||
*/
|
||||
protected abstract class UdpRequestThread extends Thread {
|
||||
|
||||
protected final DatagramPacket request;
|
||||
protected final DatagramSocket socket;
|
||||
|
||||
protected UdpRequestThread(DatagramSocket socket, DatagramPacket request) {
|
||||
this.socket = socket;
|
||||
this.request = request;
|
||||
this.setDaemon(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new UdpRequestThread to handle a DatagramPacket received
|
||||
* through a DatagramSocket.
|
||||
*
|
||||
* @param socket the socket through which the request was received.
|
||||
* @param request the datagram packet received through the socket.
|
||||
* @return a new UdpRequestThread to handle the request received through
|
||||
* a DatagramSocket.
|
||||
*/
|
||||
protected abstract UdpRequestThread createConnection(DatagramSocket socket,
|
||||
DatagramPacket request);
|
||||
|
||||
/**
|
||||
* Creates and starts a new UdpRequestThread to handle the received
|
||||
* datagram packet.
|
||||
*
|
||||
* @param socket the socket through which the request was received.
|
||||
* @param request the datagram packet received through the socket.
|
||||
*/
|
||||
private synchronized void handle(DatagramSocket socket,
|
||||
DatagramPacket request) {
|
||||
UdpRequestThread c = createConnection(socket, request);
|
||||
// c can be null if the request requires no response.
|
||||
if (c != null) {
|
||||
c.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the server socket.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public synchronized void close() throws IOException {
|
||||
if (serverSocket != null && !serverSocket.isClosed()) {
|
||||
serverSocket.close();
|
||||
}
|
||||
if (acceptThread != null) {
|
||||
acceptThread.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A small UDP Server that emulates the discard service for tests purposes.
|
||||
* See http://en.wikipedia.org/wiki/Discard_Protocol This server uses an
|
||||
* anonymous port - NOT the standard port 9. We don't guarantee that its
|
||||
* behavior exactly matches the RFC - the only purpose of this server is to
|
||||
* have something that responds to nio tests...
|
||||
*/
|
||||
static final class UdpDiscardServer extends AbstractUdpServer {
|
||||
|
||||
public UdpDiscardServer() {
|
||||
this(0L);
|
||||
}
|
||||
|
||||
public UdpDiscardServer(long linger) {
|
||||
super(linger);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UdpRequestThread createConnection(DatagramSocket socket,
|
||||
DatagramPacket request) {
|
||||
// no response required
|
||||
return null;
|
||||
}
|
||||
|
||||
public static UdpDiscardServer startNewServer() throws IOException {
|
||||
return startNewServer(0);
|
||||
}
|
||||
|
||||
public static UdpDiscardServer startNewServer(long linger) throws IOException {
|
||||
final UdpDiscardServer discardServer = new UdpDiscardServer(linger);
|
||||
discardServer.start();
|
||||
return discardServer;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A small UDP Server that emulates the echo service for tests purposes. See
|
||||
* http://en.wikipedia.org/wiki/Echo_Protocol This server uses an anonymous
|
||||
* port - NOT the standard port 7. We don't guarantee that its behavior
|
||||
* exactly matches the RFC - the only purpose of this server is to have
|
||||
* something that responds to nio tests...
|
||||
*/
|
||||
static final class UdpEchoServer extends AbstractUdpServer {
|
||||
|
||||
public UdpEchoServer() {
|
||||
this(0L);
|
||||
}
|
||||
|
||||
public UdpEchoServer(long linger) {
|
||||
this(linger, null);
|
||||
}
|
||||
|
||||
public UdpEchoServer(long linger, InetAddress bindAddress) {
|
||||
super(linger, bindAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UdpEchoRequest createConnection(DatagramSocket socket,
|
||||
DatagramPacket request) {
|
||||
return new UdpEchoRequest(socket, request);
|
||||
}
|
||||
|
||||
private final class UdpEchoRequest extends UdpRequestThread {
|
||||
|
||||
public UdpEchoRequest(DatagramSocket socket, DatagramPacket request) {
|
||||
super(socket, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
lingerIfRequired();
|
||||
final DatagramPacket response =
|
||||
new DatagramPacket(request.getData(),
|
||||
request.getOffset(), request.getLength(),
|
||||
request.getAddress(), request.getPort());
|
||||
send(socket, response);
|
||||
} catch (IOException io) {
|
||||
System.err.println("Failed to send response: " + io);
|
||||
io.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static UdpEchoServer startNewServer() throws IOException {
|
||||
return startNewServer(0);
|
||||
}
|
||||
|
||||
public static UdpEchoServer startNewServer(long linger) throws IOException {
|
||||
return startNewServer(0, InetAddress.getLocalHost());
|
||||
}
|
||||
|
||||
public static UdpEchoServer startNewServer(long linger, InetAddress bindAddress) throws IOException {
|
||||
final UdpEchoServer echoServer = new UdpEchoServer(linger, bindAddress);
|
||||
echoServer.start();
|
||||
return echoServer;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A small UDP server that emulates the Day & Time service for tests
|
||||
* purposes. See http://en.wikipedia.org/wiki/Daytime_Protocol This server
|
||||
* uses an anonymous port - NOT the standard port 13. We don't guarantee
|
||||
* that its behavior exactly matches the RFC - the only purpose of this
|
||||
* server is to have something that responds to nio tests...
|
||||
*/
|
||||
static final class UdpDayTimeServer extends AbstractUdpServer {
|
||||
|
||||
public UdpDayTimeServer() {
|
||||
this(0L);
|
||||
}
|
||||
|
||||
public UdpDayTimeServer(long linger) {
|
||||
super(linger);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UdpDayTimeRequestThread createConnection(DatagramSocket socket,
|
||||
DatagramPacket request) {
|
||||
return new UdpDayTimeRequestThread(socket, request);
|
||||
}
|
||||
|
||||
private final class UdpDayTimeRequestThread extends UdpRequestThread {
|
||||
|
||||
public UdpDayTimeRequestThread(DatagramSocket socket,
|
||||
DatagramPacket request) {
|
||||
super(socket, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
lingerIfRequired();
|
||||
final byte[] data = new Date(System.currentTimeMillis())
|
||||
.toString().getBytes("US-ASCII");
|
||||
final DatagramPacket response =
|
||||
new DatagramPacket(data, 0, data.length,
|
||||
request.getAddress(), request.getPort());
|
||||
send(socket, response);
|
||||
} catch (IOException io) {
|
||||
System.err.println("Failed to send response: " + io);
|
||||
io.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static UdpDayTimeServer startNewServer() throws IOException {
|
||||
return startNewServer(0);
|
||||
}
|
||||
|
||||
public static UdpDayTimeServer startNewServer(long linger)
|
||||
throws IOException {
|
||||
final UdpDayTimeServer echoServer = new UdpDayTimeServer(linger);
|
||||
echoServer.start();
|
||||
return echoServer;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue