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