FileDocCategorySizeDatePackage
AbstractSessionContext.javaAPI DocAndroid 1.5 API6190Wed May 06 22:41:06 BST 2009org.apache.harmony.xnet.provider.jsse

AbstractSessionContext.java

/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.harmony.xnet.provider.jsse;

import java.util.*;
import java.util.logging.Level;
import java.io.*;

import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
import javax.security.cert.X509Certificate;
import javax.security.cert.CertificateEncodingException;
import javax.security.cert.CertificateException;

/**
 * Supports SSL session caches.
 */
abstract class AbstractSessionContext implements SSLSessionContext {

    volatile int maximumSize;
    volatile int timeout;

    final SSLParameters parameters;

    /** Identifies OpenSSL sessions. */
    static final int OPEN_SSL = 1;

    /**
     * Constructs a new session context.
     *
     * @param parameters
     * @param maximumSize of cache
     * @param timeout for cache entries
     */
    AbstractSessionContext(SSLParameters parameters, int maximumSize,
            int timeout) {
        this.parameters = parameters;
        this.maximumSize = maximumSize;
        this.timeout = timeout;
    }

    /**
     * Returns the collection of sessions ordered by least-recently-used first.
     */
    abstract Iterator<SSLSession> sessionIterator();

    public final Enumeration getIds() {
        final Iterator<SSLSession> iterator = sessionIterator();
        return new Enumeration<byte[]>() {
            public boolean hasMoreElements() {
                return iterator.hasNext();
            }
            public byte[] nextElement() {
                return iterator.next().getId();
            }
        };
    }

    public final int getSessionCacheSize() {
        return maximumSize;
    }

    public final int getSessionTimeout() {
        return timeout;
    }

    /**
     * Makes sure cache size is < maximumSize.
     */
    abstract void trimToSize();

    public final void setSessionCacheSize(int size)
            throws IllegalArgumentException {
        if (size < 0) {
            throw new IllegalArgumentException("size < 0");
        }

        int oldMaximum = maximumSize;
        maximumSize = size;

        // Trim cache to size if necessary.
        if (size < oldMaximum) {
            trimToSize();
        }
    }

    /**
     * Converts the given session to bytes.
     *
     * @return session data as bytes or null if the session can't be converted
     */
    byte[] toBytes(SSLSession session) {
        // TODO: Support SSLSessionImpl, too.
        if (!(session instanceof OpenSSLSessionImpl)) {
            return null;
        }

        OpenSSLSessionImpl sslSession = (OpenSSLSessionImpl) session;
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            DataOutputStream daos = new DataOutputStream(baos);

            daos.writeInt(OPEN_SSL); // session type ID

            // Session data.
            byte[] data = sslSession.getEncoded();
            daos.writeInt(data.length);
            daos.write(data);

            // Certificates.
            X509Certificate[] certs = session.getPeerCertificateChain();
            daos.writeInt(certs.length);

            // TODO: Call nativegetpeercertificates()
            for (X509Certificate cert : certs) {
                data = cert.getEncoded();
                daos.writeInt(data.length);
                daos.write(data);
            }

            return baos.toByteArray();
        } catch (IOException e) {
            log(e);
            return null;
        } catch (CertificateEncodingException e) {
            log(e);
            return null;
        }
    }

    /**
     * Creates a session from the given bytes.
     *
     * @return a session or null if the session can't be converted
     */
    SSLSession toSession(byte[] data, String host, int port) {
        ByteArrayInputStream bais = new ByteArrayInputStream(data);
        DataInputStream dais = new DataInputStream(bais);
        try {
            int type = dais.readInt();
            if (type != OPEN_SSL) {
                log(new AssertionError("Unexpected type ID: " + type));
                return null;
            }

            int length = dais.readInt();
            byte[] sessionData = new byte[length];
            dais.readFully(sessionData);

            int count = dais.readInt();
            X509Certificate[] certs = new X509Certificate[count];
            for (int i = 0; i < count; i++) {
                length = dais.readInt();
                byte[] certData = new byte[length];
                dais.readFully(certData);
                certs[i] = X509Certificate.getInstance(certData);
            }

            return new OpenSSLSessionImpl(sessionData, parameters, host, port,
                    certs, this);
        } catch (IOException e) {
            log(e);
            return null;
        } catch (CertificateException e) {
            log(e);
            return null;
        }
    }

    static void log(Throwable t) {
        java.util.logging.Logger.global.log(Level.WARNING,
                "Error converting session.", t);
    }

    /**
     * Byte array wrapper. Implements equals() and hashCode().
     */
    static class ByteArray {

        private final byte[] bytes;

        ByteArray(byte[] bytes) {
            this.bytes = bytes;
        }

        @Override
        public int hashCode() {
            return Arrays.hashCode(bytes);
        }

        @Override
        @SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
        public boolean equals(Object o) {
            ByteArray other = (ByteArray) o;
            return Arrays.equals(bytes, other.bytes);
        }
    }
}