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
28import java.io.*;
29import java.net.*;
30import java.util.ArrayList;
31import java.util.Date;
32import java.util.List;
33
34
35public 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}
864