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 java.util.Map; 21import 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 */ 27public 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