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.SSLSession;
     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 public class ClientSessionContext extends AbstractSessionContext {
     28 
     29     /**
     30      * Sessions indexed by host and port. Protect from concurrent
     31      * access by holding a lock on sessionsByHostAndPort.
     32      */
     33     final Map<HostAndPort, SSLSession> sessionsByHostAndPort
     34         = new HashMap<HostAndPort, SSLSession>();
     35 
     36     private SSLClientSessionCache persistentCache;
     37 
     38     public ClientSessionContext() {
     39         super(10);
     40     }
     41 
     42     public int size() {
     43         return sessionsByHostAndPort.size();
     44     }
     45 
     46     public void setPersistentCache(SSLClientSessionCache persistentCache) {
     47         this.persistentCache = persistentCache;
     48     }
     49 
     50     @Override
     51     protected void sessionRemoved(SSLSession session) {
     52         String host = session.getPeerHost();
     53         int port = session.getPeerPort();
     54         if (host == null) {
     55             return;
     56         }
     57         HostAndPort hostAndPortKey = new HostAndPort(host, port);
     58         synchronized (sessionsByHostAndPort) {
     59             sessionsByHostAndPort.remove(hostAndPortKey);
     60         }
     61     }
     62 
     63     /**
     64      * Finds a cached session for the given host name and port.
     65      *
     66      * @param host of server
     67      * @param port of server
     68      * @return cached session or null if none found
     69      */
     70     public SSLSession getSession(String host, int port) {
     71         if (host == null) {
     72             return null;
     73         }
     74         SSLSession session;
     75         HostAndPort hostAndPortKey = new HostAndPort(host, port);
     76         synchronized (sessionsByHostAndPort) {
     77             session = sessionsByHostAndPort.get(hostAndPortKey);
     78         }
     79         if (session != null && session.isValid()) {
     80             return session;
     81         }
     82 
     83         // Look in persistent cache.
     84         if (persistentCache != null) {
     85             byte[] data = persistentCache.getSessionData(host, port);
     86             if (data != null) {
     87                 session = toSession(data, host, port);
     88                 if (session != null && session.isValid()) {
     89                     super.putSession(session);
     90                     synchronized (sessionsByHostAndPort) {
     91                         sessionsByHostAndPort.put(hostAndPortKey, session);
     92                     }
     93                     return session;
     94                 }
     95             }
     96         }
     97 
     98         return null;
     99     }
    100 
    101     @Override
    102     public void putSession(SSLSession session) {
    103         super.putSession(session);
    104 
    105         String host = session.getPeerHost();
    106         int port = session.getPeerPort();
    107         if (host == null) {
    108             return;
    109         }
    110 
    111         HostAndPort hostAndPortKey = new HostAndPort(host, port);
    112         synchronized (sessionsByHostAndPort) {
    113             sessionsByHostAndPort.put(hostAndPortKey, session);
    114         }
    115 
    116         // TODO: This in a background thread.
    117         if (persistentCache != null) {
    118             byte[] data = toBytes(session);
    119             if (data != null) {
    120                 persistentCache.putSessionData(session, data);
    121             }
    122         }
    123     }
    124 
    125     static class HostAndPort {
    126         final String host;
    127         final int port;
    128 
    129         HostAndPort(String host, int port) {
    130             this.host = host;
    131             this.port = port;
    132         }
    133 
    134         @Override
    135         public int hashCode() {
    136             return host.hashCode() * 31 + port;
    137         }
    138 
    139         @Override
    140         public boolean equals(Object o) {
    141             if (!(o instanceof HostAndPort)) {
    142                 return false;
    143             }
    144             HostAndPort lhs = (HostAndPort) o;
    145             return host.equals(lhs.host) && port == lhs.port;
    146         }
    147     }
    148 }
    149