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