1/*
2 * Copyright (c) 2001, 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/*
25 * @test
26 * @summary Test behavior related to finalize
27 * @run main/othervm  SSLSessionFinalizeTest
28 * @run main/othervm/policy=security.policy  SSLSessionFinalizeTest
29 */
30
31import java.io.InputStream;
32import java.io.OutputStream;
33import java.lang.ref.Reference;
34import java.lang.ref.WeakReference;
35import java.util.concurrent.ArrayBlockingQueue;
36
37import javax.net.ssl.SSLServerSocket;
38import javax.net.ssl.SSLServerSocketFactory;
39import javax.net.ssl.SSLSession;
40import javax.net.ssl.SSLSessionBindingEvent;
41import javax.net.ssl.SSLSessionBindingListener;
42import javax.net.ssl.SSLSocket;
43import javax.net.ssl.SSLSocketFactory;
44
45public class SSLSessionFinalizeTest {
46
47    /*
48     * =============================================================
49     * Set the various variables needed for the tests, then
50     * specify what tests to run on each side.
51     */
52
53    /*
54     * Should we run the client or server in a separate thread?
55     * Both sides can throw exceptions, but do you have a preference
56     * as to which side should be the main thread.
57     */
58    static boolean separateServerThread = true;
59
60    /*
61     * Where do we find the keystores?
62     */
63    static String pathToStores = "../etc";
64    static String keyStoreFile = "keystore";
65    static String trustStoreFile = "truststore";
66    static String passwd = "passphrase";
67
68    /*
69     * Is the server ready to serve?
70     */
71    volatile static boolean serverReady = false;
72
73    /*
74     * Turn on SSL debugging?
75     */
76    static boolean debug = false;
77
78    /*
79     * If the client or server is doing some kind of object creation
80     * that the other side depends on, and that thread prematurely
81     * exits, you may experience a hang.  The test harness will
82     * terminate all hung threads after its timeout has expired,
83     * currently 3 minutes by default, but you might try to be
84     * smart about it....
85     */
86
87    /*
88     * Define the server side of the test.
89     *
90     * If the server prematurely exits, serverReady will be set to true
91     * to avoid infinite hangs.
92     */
93    void doServerSide() throws Exception {
94        SSLServerSocketFactory sslssf =
95            (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
96        SSLServerSocket sslServerSocket =
97            (SSLServerSocket) sslssf.createServerSocket(serverPort);
98        serverPort = sslServerSocket.getLocalPort();
99
100        /*
101         * Signal Client, we're ready for his connect.
102         */
103        serverReady = true;
104
105        while (serverReady) {
106            SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
107//            System.out.printf("  accept: %s%n", sslSocket);
108            InputStream sslIS = sslSocket.getInputStream();
109            OutputStream sslOS = sslSocket.getOutputStream();
110
111            sslIS.read();
112            sslOS.write(85);
113            sslOS.flush();
114
115            sslSocket.close();
116        }
117    }
118
119    /*
120     * Define the client side of the test.
121     *
122     * If the server prematurely exits, serverReady will be set to true
123     * to avoid infinite hangs.
124     */
125    SBListener doClientSide() throws Exception {
126
127        /*
128         * Wait for server to get started.
129         */
130        while (!serverReady) {
131            Thread.sleep(50);
132        }
133
134        SSLSocketFactory sslsf =
135            (SSLSocketFactory) SSLSocketFactory.getDefault();
136
137        try {
138                SSLSocket sslSocket = (SSLSocket)
139                    sslsf.createSocket("localhost", serverPort);
140                InputStream sslIS = sslSocket.getInputStream();
141                OutputStream sslOS = sslSocket.getOutputStream();
142
143            sslOS.write(280);
144            sslOS.flush();
145            sslIS.read();
146
147            sslOS.close();
148            sslIS.close();
149
150            SSLSession sslSession = sslSocket.getSession();
151            System.out.printf(" sslSession: %s %n   %s%n", sslSession, sslSession.getClass());
152            SBListener sbListener = new SBListener(sslSession);
153
154            sslSession.putValue("x", sbListener);
155
156            sslSession.invalidate();
157
158            sslSocket.close();
159
160            sslOS = null;
161            sslIS = null;
162            sslSession = null;
163            sslSocket = null;
164            Reference.reachabilityFence(sslOS);
165            Reference.reachabilityFence(sslIS);
166            Reference.reachabilityFence(sslSession);
167            Reference.reachabilityFence(sslSocket);
168
169            return sbListener;
170        } catch (Exception ex) {
171            ex.printStackTrace();
172            throw ex;
173        }
174    }
175
176    /*
177     * =============================================================
178     * The remainder is just support stuff
179     */
180
181    // use any free port by default
182    volatile int serverPort = 0;
183
184    volatile Exception serverException = null;
185    volatile Exception clientException = null;
186
187    public static void main(String[] args) throws Exception {
188        String keyFilename =
189            System.getProperty("test.src", "./") + "/" + pathToStores +
190                "/" + keyStoreFile;
191        String trustFilename =
192            System.getProperty("test.src", "./") + "/" + pathToStores +
193                "/" + trustStoreFile;
194
195        System.setProperty("javax.net.ssl.keyStore", keyFilename);
196        System.setProperty("javax.net.ssl.keyStorePassword", passwd);
197        System.setProperty("javax.net.ssl.trustStore", trustFilename);
198        System.setProperty("javax.net.ssl.trustStorePassword", passwd);
199
200        if (debug)
201            System.setProperty("javax.net.debug", "all");
202
203        /*
204         * Start the tests.
205         */
206        new SSLSessionFinalizeTest();
207    }
208
209    ArrayBlockingQueue<Thread> threads = new ArrayBlockingQueue<Thread>(100);
210
211    ArrayBlockingQueue<SBListener> sbListeners = new ArrayBlockingQueue<>(100);
212
213    /*
214     * Primary constructor, used to drive remainder of the test.
215     *
216     * Fork off the other side, then do your work.
217     */
218    SSLSessionFinalizeTest() throws Exception {
219        final int count = 1;
220        if (separateServerThread) {
221            startServer(true);
222            startClients(true, count);
223        } else {
224            startClients(true, count);
225            startServer(true);
226        }
227
228        /*
229         * Wait for other side to close down.
230         */
231        Thread t;
232        while ((t = threads.take()) != Thread.currentThread()) {
233            System.out.printf("  joining: %s%n", t);
234            t.join(1000L);
235        }
236        serverReady = false;
237        System.gc();
238        System.gc();
239
240
241        SBListener listener = null;
242        while ((listener = sbListeners.poll()) != null) {
243            if (!listener.check()) {
244                System.out.printf("  sbListener not called on finalize: %s%n",
245                        listener);
246            }
247        }
248
249        /*
250         * When we get here, the test is pretty much over.
251         *
252         * If the main thread excepted, that propagates back
253         * immediately.  If the other thread threw an exception, we
254         * should report back.
255         */
256        if (serverException != null) {
257            System.out.print("Server Exception:");
258            throw serverException;
259        }
260        if (clientException != null) {
261            System.out.print("Client Exception:");
262            throw clientException;
263        }
264    }
265
266    void startServer(boolean newThread) throws Exception {
267        if (newThread) {
268            Thread t = new Thread("Server") {
269                public void run() {
270                    try {
271                        doServerSide();
272                    } catch (Exception e) {
273                        /*
274                         * Our server thread just died.
275                         *
276                         * Release the client, if not active already...
277                         */
278                        System.err.println("Server died..." + e);
279                        serverReady = true;
280                        serverException = e;
281                    }
282                }
283            };
284            threads.add(t);
285            t.setDaemon(true);
286            t.start();
287        } else {
288            doServerSide();
289        }
290    }
291
292    void startClients(boolean newThread, int count) throws Exception {
293        for (int i = 0; i < count; i++) {
294            System.out.printf(" newClient: %d%n", i);
295            startClient(newThread);
296        }
297        serverReady = false;
298
299        threads.add(Thread.currentThread());    // add ourselves at the 'end'
300    }
301    void startClient(boolean newThread) throws Exception {
302        if (newThread) {
303            Thread t = new Thread("Client") {
304                public void run() {
305                    try {
306                        sbListeners.add(doClientSide());
307                    } catch (Exception e) {
308                        /*
309                         * Our client thread just died.
310                         */
311                        System.err.println("Client died..." + e);
312                        clientException = e;
313                    }
314                }
315            };
316            System.out.printf(" starting: %s%n", t);
317            threads.add(t);
318            t.start();
319        } else {
320            sbListeners.add(doClientSide());
321        }
322    }
323
324
325    static class SBListener implements SSLSessionBindingListener {
326        private volatile int unboundNotified;
327        private final WeakReference<SSLSession> session;
328
329        SBListener(SSLSession session) {
330            this.unboundNotified = 0;
331            this.session = new WeakReference<SSLSession>(session);
332        }
333
334        boolean check() {
335            System.out.printf("  check: %s%n", this);
336            return unboundNotified > 0 && session.get() == null;
337        }
338
339        @Override
340        public void valueBound(SSLSessionBindingEvent event) {
341            System.out.printf(" valueBound: %s%n", event.getName());
342        }
343
344        @Override
345        public void valueUnbound(SSLSessionBindingEvent event) {
346            System.out.printf(" valueUnbound: %s%n", event.getName());
347            unboundNotified++;
348        }
349
350        public String toString() {
351            return "count: " + unboundNotified +
352                    ", ref: " + session.get();
353        }
354    }
355}
356