disable broken tests on net_4_0
[mcs.git] / class / System.ServiceModel / Mono.Security.Protocol.Tls / ClientSessionCache.cs
blob749a51edea4a3094ec58e8dd19eaafac2da4eb8a
1 //
2 // ClientSessionCache.cs: Client-side cache for re-using sessions
3 //
4 // Author:
5 // Sebastien Pouliot <sebastien@ximian.com>
6 //
7 // Copyright (C) 2006 Novell (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 using System;
30 using System.Collections;
32 namespace Mono.Security.Protocol.Tls {
34 internal class ClientSessionInfo : IDisposable {
36 // (by default) we keep this item valid for 3 minutes (if unused)
37 private const int DefaultValidityInterval = 3 * 60;
38 private static readonly int ValidityInterval;
40 private bool disposed;
41 private DateTime validuntil;
42 private string host;
44 // see RFC2246 - Section 7
45 private byte[] sid;
46 private byte[] masterSecret;
48 static ClientSessionInfo ()
50 string user_cache_timeout = Environment.GetEnvironmentVariable ("MONO_TLS_SESSION_CACHE_TIMEOUT");
51 if (user_cache_timeout == null) {
52 ValidityInterval = DefaultValidityInterval;
53 } else {
54 try {
55 ValidityInterval = Int32.Parse (user_cache_timeout);
57 catch {
58 ValidityInterval = DefaultValidityInterval;
63 public ClientSessionInfo (string hostname, byte[] id)
65 host = hostname;
66 sid = id;
67 KeepAlive ();
70 ~ClientSessionInfo ()
72 Dispose (false);
76 public string HostName {
77 get { return host; }
80 public byte[] Id {
81 get { return sid; }
84 public bool Valid {
85 get { return ((masterSecret != null) && (validuntil > DateTime.UtcNow)); }
89 public void GetContext (Context context)
91 CheckDisposed ();
92 if (context.MasterSecret != null)
93 masterSecret = (byte[]) context.MasterSecret.Clone ();
96 public void SetContext (Context context)
98 CheckDisposed ();
99 if (masterSecret != null)
100 context.MasterSecret = (byte[]) masterSecret.Clone ();
103 public void KeepAlive ()
105 CheckDisposed ();
106 validuntil = DateTime.UtcNow.AddSeconds (ValidityInterval);
109 public void Dispose ()
111 Dispose (true);
112 GC.SuppressFinalize (this);
115 private void Dispose (bool disposing)
117 if (!disposed) {
118 validuntil = DateTime.MinValue;
119 host = null;
120 sid = null;
122 if (masterSecret != null) {
123 Array.Clear (masterSecret, 0, masterSecret.Length);
124 masterSecret = null;
127 disposed = true;
130 private void CheckDisposed ()
132 if (disposed) {
133 string msg = Locale.GetText ("Cache session information were disposed.");
134 throw new ObjectDisposedException (msg);
139 // note: locking is aggressive but isn't used often (and we gain much more :)
140 internal class ClientSessionCache {
142 static Hashtable cache;
143 static object locker;
145 static ClientSessionCache ()
147 cache = new Hashtable ();
148 locker = new object ();
151 // note: we may have multiple connections with a host, so
152 // possibly multiple entries per host (each with a different
153 // id), so we do not use the host as the hashtable key
154 static public void Add (string host, byte[] id)
156 lock (locker) {
157 string uid = BitConverter.ToString (id);
158 ClientSessionInfo si = (ClientSessionInfo) cache[uid];
159 if (si == null) {
160 cache.Add (uid, new ClientSessionInfo (host, id));
161 } else if (si.HostName == host) {
162 // we already have this and it's still valid
163 // on the server, so we'll keep it a little longer
164 si.KeepAlive ();
165 } else {
166 // it's very unlikely but the same session id
167 // could be used by more than one host. In this
168 // case we replace the older one with the new one
169 si.Dispose ();
170 cache.Remove (uid);
171 cache.Add (uid, new ClientSessionInfo (host, id));
176 // return the first session us
177 static public byte[] FromHost (string host)
179 lock (locker) {
180 foreach (ClientSessionInfo si in cache.Values) {
181 if (si.HostName == host) {
182 if (si.Valid) {
183 // ensure it's still valid when we really need it
184 si.KeepAlive ();
185 return si.Id;
189 return null;
193 // only called inside the lock
194 static private ClientSessionInfo FromContext (Context context, bool checkValidity)
196 if (context == null)
197 return null;
199 byte[] id = context.SessionId;
200 if ((id == null) || (id.Length == 0))
201 return null;
203 // do we have a session cached for this host ?
204 string uid = BitConverter.ToString (id);
206 ClientSessionInfo si = (ClientSessionInfo) cache[uid];
207 if (si == null)
208 return null;
210 // In the unlikely case of multiple hosts using the same
211 // session id, we just act like we do not know about it
212 if (context.ClientSettings.TargetHost != si.HostName)
213 return null;
215 // yes, so what's its status ?
216 if (checkValidity && !si.Valid) {
217 si.Dispose ();
218 cache.Remove (uid);
219 return null;
222 // ok, it make sense
223 return si;
226 static public bool SetContextInCache (Context context)
228 lock (locker) {
229 // Don't check the validity because the masterKey of the ClientSessionInfo
230 // can still be null when this is called the first time
231 ClientSessionInfo csi = FromContext (context, false);
232 if (csi == null)
233 return false;
235 csi.GetContext (context);
236 csi.KeepAlive ();
237 return true;
241 static public bool SetContextFromCache (Context context)
243 lock (locker) {
244 ClientSessionInfo csi = FromContext (context, true);
245 if (csi == null)
246 return false;
248 csi.SetContext (context);
249 csi.KeepAlive ();
250 return true;