SessionTimeOutTests.java revision 13470:c20bc888feea
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// SunJSSE does not support dynamic system properties, no way to re-use
25// system properties in samevm/agentvm mode.
26
27/*
28 * @test
29 * @bug   4366807
30 * @summary Need new APIs to get/set session timeout and session cache size.
31 * @run main/othervm SessionTimeOutTests
32 */
33
34import java.io.*;
35import java.net.*;
36import javax.net.ssl.*;
37import java.util.*;
38import java.security.*;
39import java.util.concurrent.atomic.AtomicInteger;
40
41/**
42 * Session reuse time-out tests cover the cases below:
43 * 1. general test, i.e timeout is set to x and session invalidates when
44 * its lifetime exceeds x.
45 * 2. Effect of changing the timeout limit.
46 * The test suite does not cover the default timeout(24 hours) usage. This
47 * case has been tested independetly.
48 *
49 * Invairant for passing this test is, at any given time,
50 * lifetime of a session < current_session_timeout, such that
51 * current_session_timeout > 0, for all sessions cached by the session
52 * context.
53 */
54
55public class SessionTimeOutTests {
56
57    /*
58     * =============================================================
59     * Set the various variables needed for the tests, then
60     * specify what tests to run on each side.
61     */
62
63    /*
64     * Should we run the client or server in a separate thread?
65     * Both sides can throw exceptions, but do you have a preference
66     * as to which side should be the main thread.
67     */
68    static boolean separateServerThread = true;
69
70    /*
71     * Where do we find the keystores?
72     */
73    static String pathToStores = "../etc";
74    static String keyStoreFile = "keystore";
75    static String trustStoreFile = "truststore";
76    static String passwd = "passphrase";
77
78    private static int PORTS = 3;
79
80    /*
81     * Is the server ready to serve?
82     */
83    AtomicInteger serverReady = new AtomicInteger(PORTS);
84
85    /*
86     * Turn on SSL debugging?
87     */
88    static boolean debug = false;
89
90    /*
91     * If the client or server is doing some kind of object creation
92     * that the other side depends on, and that thread prematurely
93     * exits, you may experience a hang.  The test harness will
94     * terminate all hung threads after its timeout has expired,
95     * currently 3 minutes by default, but you might try to be
96     * smart about it....
97     */
98
99    /*
100     * Define the server side of the test.
101     *
102     * If the server prematurely exits, serverReady will be set to zero
103     * to avoid infinite hangs.
104     */
105
106    /*
107     * A limit on the number of connections at any given time
108     */
109    static int MAX_ACTIVE_CONNECTIONS = 3;
110
111    void doServerSide(int serverPort, int serverConns) throws Exception {
112
113        SSLServerSocket sslServerSocket =
114            (SSLServerSocket) sslssf.createServerSocket(serverPort);
115        int slot = createdPorts.getAndIncrement();
116        serverPorts[slot] = sslServerSocket.getLocalPort();
117
118        /*
119         * Signal Client, we're ready for his connect.
120         */
121        serverReady.getAndDecrement();
122        int read = 0;
123        int nConnections = 0;
124        SSLSession sessions [] = new SSLSession [serverConns];
125
126        while (nConnections < serverConns) {
127            SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
128            InputStream sslIS = sslSocket.getInputStream();
129            OutputStream sslOS = sslSocket.getOutputStream();
130            read = sslIS.read();
131            sessions[nConnections] = sslSocket.getSession();
132            sslOS.write(85);
133            sslOS.flush();
134            sslSocket.close();
135            nConnections++;
136        }
137    }
138
139    /*
140     * Define the client side of the test.
141     *
142     * If the server prematurely exits, serverReady will be set to zero
143     * to avoid infinite hangs.
144     */
145    void doClientSide() throws Exception {
146
147        /*
148         * Wait for server to get started.
149         */
150        while (serverReady.get() > 0) {
151            Thread.sleep(50);
152        }
153
154        int nConnections = 0;
155        SSLSocket sslSockets[] = new SSLSocket [MAX_ACTIVE_CONNECTIONS];
156        Vector sessions = new Vector();
157        SSLSessionContext sessCtx = sslctx.getClientSessionContext();
158
159        sessCtx.setSessionTimeout(10); // in secs
160        int timeout = sessCtx.getSessionTimeout();
161        while (nConnections < MAX_ACTIVE_CONNECTIONS) {
162            // divide the connections among the available server ports
163            sslSockets[nConnections] = (SSLSocket) sslsf.
164                        createSocket("localhost",
165                        serverPorts [nConnections % (serverPorts.length)]);
166            InputStream sslIS = sslSockets[nConnections].getInputStream();
167            OutputStream sslOS = sslSockets[nConnections].getOutputStream();
168            sslOS.write(237);
169            sslOS.flush();
170            int read = sslIS.read();
171            SSLSession sess = sslSockets[nConnections].getSession();
172            if (!sessions.contains(sess))
173                sessions.add(sess);
174            nConnections++;
175        }
176        System.out.println();
177        System.out.println("Current timeout is set to: " + timeout);
178        System.out.println("Testing SSLSessionContext.getSession()......");
179        System.out.println("========================================"
180                                + "=======================");
181        System.out.println("Session                                 "
182                                + "Session-     Session");
183        System.out.println("                                        "
184                                + "lifetime     timedout?");
185        System.out.println("========================================"
186                                + "=======================");
187
188        for (int i = 0; i < sessions.size(); i++) {
189            SSLSession session = (SSLSession) sessions.elementAt(i);
190            long currentTime  = System.currentTimeMillis();
191            long creationTime = session.getCreationTime();
192            long lifetime = (currentTime - creationTime) / 1000;
193
194            System.out.print(session + "      " + lifetime + "            ");
195            if (sessCtx.getSession(session.getId()) == null) {
196                if (lifetime < timeout)
197                    // sessions can be garbage collected before the timeout
198                    // limit is reached
199                    System.out.println("Invalidated before timeout");
200                else
201                    System.out.println("YES");
202            } else {
203                System.out.println("NO");
204                if ((timeout != 0) && (lifetime > timeout)) {
205                    throw new Exception("Session timeout test failed for the"
206                        + " obove session");
207                }
208            }
209            // change the timeout
210            if (i == ((sessions.size()) / 2)) {
211                System.out.println();
212                sessCtx.setSessionTimeout(2); // in secs
213                timeout = sessCtx.getSessionTimeout();
214                System.out.println("timeout is changed to: " + timeout);
215                System.out.println();
216           }
217        }
218
219        // check the ids returned by the enumerator
220        Enumeration e = sessCtx.getIds();
221        System.out.println("----------------------------------------"
222                                + "-----------------------");
223        System.out.println("Testing SSLSessionContext.getId()......");
224        System.out.println();
225
226        SSLSession nextSess = null;
227        SSLSession sess;
228        for (int i = 0; i < sessions.size(); i++) {
229            sess = (SSLSession)sessions.elementAt(i);
230            String isTimedout = "YES";
231            long currentTime  = System.currentTimeMillis();
232            long creationTime  = sess.getCreationTime();
233            long lifetime = (currentTime - creationTime) / 1000;
234
235            if (nextSess != null) {
236                if (isEqualSessionId(nextSess.getId(), sess.getId())) {
237                    isTimedout = "NO";
238                    nextSess = null;
239                }
240            } else if (e.hasMoreElements()) {
241                nextSess = sessCtx.getSession((byte[]) e.nextElement());
242                if ((nextSess != null) && isEqualSessionId(nextSess.getId(),
243                                        sess.getId())) {
244                    nextSess = null;
245                    isTimedout = "NO";
246                }
247            }
248
249            /*
250             * A session not invalidated even after it's timeout?
251             */
252            if ((timeout != 0) && (lifetime > timeout) &&
253                        (isTimedout.equals("NO"))) {
254                throw new Exception("Session timeout test failed for session: "
255                                + sess + " lifetime: " + lifetime);
256            }
257            System.out.print(sess + "      " + lifetime);
258            if (((timeout == 0) || (lifetime < timeout)) &&
259                                  (isTimedout == "YES")) {
260                isTimedout = "Invalidated before timeout";
261            }
262
263            System.out.println("            " + isTimedout);
264        }
265        for (int i = 0; i < nConnections; i++) {
266            sslSockets[i].close();
267        }
268        System.out.println("----------------------------------------"
269                                 + "-----------------------");
270        System.out.println("Session timeout test passed");
271    }
272
273    boolean isEqualSessionId(byte[] id1, byte[] id2) {
274        if (id1.length != id2.length)
275           return false;
276        else {
277            for (int i = 0; i < id1.length; i++) {
278                if (id1[i] != id2[i]) {
279                   return false;
280                }
281            }
282            return true;
283        }
284    }
285
286
287    /*
288     * =============================================================
289     * The remainder is just support stuff
290     */
291
292    int serverPorts[] = new int[PORTS];
293    AtomicInteger createdPorts = new AtomicInteger(0);
294    static SSLServerSocketFactory sslssf;
295    static SSLSocketFactory sslsf;
296    static SSLContext sslctx;
297
298    volatile Exception serverException = null;
299    volatile Exception clientException = null;
300
301    public static void main(String[] args) throws Exception {
302        String keyFilename =
303            System.getProperty("test.src", "./") + "/" + pathToStores +
304                "/" + keyStoreFile;
305        String trustFilename =
306            System.getProperty("test.src", "./") + "/" + pathToStores +
307                "/" + trustStoreFile;
308
309        System.setProperty("javax.net.ssl.keyStore", keyFilename);
310        System.setProperty("javax.net.ssl.keyStorePassword", passwd);
311        System.setProperty("javax.net.ssl.trustStore", trustFilename);
312        System.setProperty("javax.net.ssl.trustStorePassword", passwd);
313
314        sslctx = SSLContext.getInstance("TLS");
315        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
316        KeyStore ks = KeyStore.getInstance("JKS");
317        ks.load(new FileInputStream(keyFilename), passwd.toCharArray());
318        kmf.init(ks, passwd.toCharArray());
319        sslctx.init(kmf.getKeyManagers(), null, null);
320        sslssf = (SSLServerSocketFactory) sslctx.getServerSocketFactory();
321        sslsf = (SSLSocketFactory) sslctx.getSocketFactory();
322        if (debug)
323            System.setProperty("javax.net.debug", "all");
324
325        /*
326         * Start the tests.
327         */
328        new SessionTimeOutTests();
329    }
330
331    Thread clientThread = null;
332    Thread serverThread = null;
333
334    /*
335     * Primary constructor, used to drive remainder of the test.
336     *
337     * Fork off the other side, then do your work.
338     */
339    SessionTimeOutTests() throws Exception {
340
341        /*
342         * create the SSLServerSocket and SSLSocket factories
343         */
344
345        /*
346         * Divide the max connections among the available server ports.
347         * The use of more than one server port ensures creation of more
348         * than one session.
349         */
350        int serverConns = MAX_ACTIVE_CONNECTIONS / (serverPorts.length);
351        int remainingConns = MAX_ACTIVE_CONNECTIONS % (serverPorts.length);
352
353        Exception startException = null;
354        try {
355            if (separateServerThread) {
356                for (int i = 0; i < serverPorts.length; i++) {
357                    // distribute remaining connections among the
358                    // vailable ports
359                    if (i < remainingConns)
360                        startServer(serverPorts[i], (serverConns + 1), true);
361                    else
362                        startServer(serverPorts[i], serverConns, true);
363                }
364                startClient(false);
365            } else {
366                startClient(true);
367                for (int i = 0; i < serverPorts.length; i++) {
368                    if (i < remainingConns)
369                        startServer(serverPorts[i], (serverConns + 1), false);
370                    else
371                        startServer(serverPorts[i], serverConns, false);
372                }
373            }
374        } catch (Exception e) {
375            startException = e;
376        }
377
378        /*
379         * Wait for other side to close down.
380         */
381        if (separateServerThread) {
382            if (serverThread != null) {
383                serverThread.join();
384            }
385        } else {
386            if (clientThread != null) {
387                clientThread.join();
388            }
389        }
390
391        /*
392         * When we get here, the test is pretty much over.
393         * Which side threw the error?
394         */
395        Exception local;
396        Exception remote;
397
398        if (separateServerThread) {
399            remote = serverException;
400            local = clientException;
401        } else {
402            remote = clientException;
403            local = serverException;
404        }
405
406        Exception exception = null;
407
408        /*
409         * Check various exception conditions.
410         */
411        if ((local != null) && (remote != null)) {
412            // If both failed, return the curthread's exception.
413            local.initCause(remote);
414            exception = local;
415        } else if (local != null) {
416            exception = local;
417        } else if (remote != null) {
418            exception = remote;
419        } else if (startException != null) {
420            exception = startException;
421        }
422
423        /*
424         * If there was an exception *AND* a startException,
425         * output it.
426         */
427        if (exception != null) {
428            if (exception != startException && startException != null) {
429                exception.addSuppressed(startException);
430            }
431            throw exception;
432        }
433
434        // Fall-through: no exception to throw!
435    }
436
437    void startServer(final int port, final int nConns,
438                        boolean newThread) throws Exception {
439        if (newThread) {
440            serverThread = new Thread() {
441                public void run() {
442                    try {
443                        doServerSide(port, nConns);
444                    } catch (Exception e) {
445                        /*
446                         * Our server thread just died.
447                         *
448                         * Release the client, if not active already...
449                         */
450                        System.err.println("Server died...");
451                        e.printStackTrace();
452                        serverReady.set(0);
453                        serverException = e;
454                    }
455                }
456            };
457            serverThread.start();
458        } else {
459            try {
460                doServerSide(port, nConns);
461            } catch (Exception e) {
462                serverException = e;
463            } finally {
464                serverReady.set(0);
465            }
466        }
467    }
468
469    void startClient(boolean newThread)
470                 throws Exception {
471        if (newThread) {
472            clientThread = new Thread() {
473                public void run() {
474                    try {
475                        doClientSide();
476                    } catch (Exception e) {
477                        /*
478                         * Our client thread just died.
479                         */
480                        System.err.println("Client died...");
481                        clientException = e;
482                    }
483                }
484            };
485            clientThread.start();
486        } else {
487            try {
488                doClientSide();
489            } catch (Exception e) {
490                clientException = e;
491            }
492        }
493    }
494}
495