Home | History | Annotate | Download | only in conscrypt
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package org.conscrypt;
     18 
     19 import java.util.HashMap;
     20 import java.util.Map;
     21 import javax.net.ssl.SSLContext;
     22 
     23 /**
     24  * Caches client sessions. Indexes by host and port. Users are typically
     25  * looking to reuse any session for a given host and port.
     26  *
     27  * @hide
     28  */
     29 @Internal
     30 public final class ClientSessionContext extends AbstractSessionContext {
     31     /**
     32      * Sessions indexed by host and port. Protect from concurrent
     33      * access by holding a lock on sessionsByHostAndPort.
     34      */
     35     @SuppressWarnings("serial")
     36     private final Map<HostAndPort, NativeSslSession> sessionsByHostAndPort = new HashMap<HostAndPort, NativeSslSession>();
     37 
     38     private SSLClientSessionCache persistentCache;
     39 
     40     ClientSessionContext() {
     41         super(10);
     42     }
     43 
     44     /**
     45      * Applications should not use this method. Instead use {@link
     46      * Conscrypt#setClientSessionCache(SSLContext, SSLClientSessionCache)}.
     47      */
     48     public void setPersistentCache(SSLClientSessionCache persistentCache) {
     49         this.persistentCache = persistentCache;
     50     }
     51 
     52     /**
     53      * Gets the suitable session reference from the session cache container.
     54      */
     55     NativeSslSession getCachedSession(String hostName, int port, SSLParametersImpl sslParameters) {
     56         if (hostName == null) {
     57             return null;
     58         }
     59 
     60         NativeSslSession session = getSession(hostName, port);
     61         if (session == null) {
     62             return null;
     63         }
     64 
     65         String protocol = session.getProtocol();
     66         boolean protocolFound = false;
     67         for (String enabledProtocol : sslParameters.enabledProtocols) {
     68             if (protocol.equals(enabledProtocol)) {
     69                 protocolFound = true;
     70                 break;
     71             }
     72         }
     73         if (!protocolFound) {
     74             return null;
     75         }
     76 
     77         String cipherSuite = session.getCipherSuite();
     78         boolean cipherSuiteFound = false;
     79         for (String enabledCipherSuite : sslParameters.enabledCipherSuites) {
     80             if (cipherSuite.equals(enabledCipherSuite)) {
     81                 cipherSuiteFound = true;
     82                 break;
     83             }
     84         }
     85         if (!cipherSuiteFound) {
     86             return null;
     87         }
     88 
     89         return session;
     90     }
     91 
     92     int size() {
     93         return sessionsByHostAndPort.size();
     94     }
     95 
     96     /**
     97      * Finds a cached session for the given host name and port.
     98      *
     99      * @param host of server
    100      * @param port of server
    101      * @return cached session or null if none found
    102      */
    103     private NativeSslSession getSession(String host, int port) {
    104         if (host == null) {
    105             return null;
    106         }
    107 
    108         HostAndPort key = new HostAndPort(host, port);
    109         NativeSslSession session;
    110         synchronized (sessionsByHostAndPort) {
    111             session = sessionsByHostAndPort.get(key);
    112         }
    113         if (session != null && session.isValid()) {
    114             return session;
    115         }
    116 
    117         // Look in persistent cache.
    118         if (persistentCache != null) {
    119             byte[] data = persistentCache.getSessionData(host, port);
    120             if (data != null) {
    121                 session = NativeSslSession.newInstance(this, data, host, port);
    122                 if (session != null && session.isValid()) {
    123                     synchronized (sessionsByHostAndPort) {
    124                         sessionsByHostAndPort.put(key, session);
    125                     }
    126                     return session;
    127                 }
    128             }
    129         }
    130 
    131         return null;
    132     }
    133 
    134     @Override
    135     void onBeforeAddSession(NativeSslSession session) {
    136         String host = session.getPeerHost();
    137         int port = session.getPeerPort();
    138         if (host == null) {
    139             return;
    140         }
    141 
    142         HostAndPort key = new HostAndPort(host, port);
    143         synchronized (sessionsByHostAndPort) {
    144             sessionsByHostAndPort.put(key, session);
    145         }
    146 
    147         // TODO: Do this in a background thread.
    148         if (persistentCache != null) {
    149             byte[] data = session.toBytes();
    150             if (data != null) {
    151                 persistentCache.putSessionData(session.toSSLSession(), data);
    152             }
    153         }
    154     }
    155 
    156     @Override
    157     void onBeforeRemoveSession(NativeSslSession session) {
    158         String host = session.getPeerHost();
    159         if (host == null) {
    160             return;
    161         }
    162         int port = session.getPeerPort();
    163         HostAndPort hostAndPortKey = new HostAndPort(host, port);
    164         synchronized (sessionsByHostAndPort) {
    165             sessionsByHostAndPort.remove(hostAndPortKey);
    166         }
    167     }
    168 
    169     @Override
    170     NativeSslSession getSessionFromPersistentCache(byte[] sessionId) {
    171         // Not implemented for clients.
    172         return null;
    173     }
    174 
    175     private static final class HostAndPort {
    176         final String host;
    177         final int port;
    178 
    179         HostAndPort(String host, int port) {
    180             this.host = host;
    181             this.port = port;
    182         }
    183 
    184         @Override
    185         public int hashCode() {
    186             return host.hashCode() * 31 + port;
    187         }
    188 
    189         @Override
    190         public boolean equals(Object o) {
    191             if (!(o instanceof HostAndPort)) {
    192                 return false;
    193             }
    194             HostAndPort lhs = (HostAndPort) o;
    195             return host.equals(lhs.host) && port == lhs.port;
    196         }
    197     }
    198 }
    199