Merge pull request #3028 from lateralusX/jlorenss/threadpool_warning
[mono.git] / mcs / class / referencesource / System / net / System / Net / _SpnDictionary.cs
1 /*++
2 Copyright (c) Microsoft Corporation
3
4 Module Name:
5
6     _SpnDictionary.cs
7
8 Abstract:
9     This internal class implements a static mutlithreaded dictionary for user-registered SPNs.
10     An SPN is mapped based on a Uri prefix that contains scheme, host and port.
11
12
13 Author:
14
15     Alexei Vopilov    15-Nov-2003
16
17 Revision History:
18
19 --*/
20
21 namespace System.Net {
22     using System;
23     using System.Collections;
24     using System.Collections.Specialized;
25     using System.Security.Permissions;
26
27     internal class SpnDictionary : StringDictionary {
28
29         //
30         //A Hashtable can support one writer and multiple readers concurrently
31         //
32
33         // Maps Uri keys to SpnToken values.  The SpnTokens should not be exposed publicly.
34         private Hashtable m_SyncTable = Hashtable.Synchronized(new Hashtable());
35         private ValueCollection m_ValuesWrapper;
36
37         //
38         //
39         internal SpnDictionary():base() {
40         }
41         //
42         //
43         //
44         public override int Count {
45             get {
46 #if !DISABLE_CAS_USE
47                 ExceptionHelper.WebPermissionUnrestricted.Demand();
48 #endif
49                 return m_SyncTable.Count;
50             }
51         }
52         //
53         // We are thread safe
54         //
55         public override bool IsSynchronized {
56             get {
57                 return true;
58             }
59         }
60         //
61         // Internal lookup, bypasses security checks
62         //
63         internal SpnToken InternalGet(string canonicalKey)
64         {
65             int lastLength = 0;
66             string key = null;
67
68             // This lock is required to avoid getting InvalidOperationException
69             // because the collection was modified during enumeration. By design 
70             // a Synchronized Hashtable throws if modifications occur while an 
71             // enumeration is in progress. Manually locking the Hashtable to 
72             // prevent modification during enumeration is the best solution. 
73             // Catching the exception and retrying could potentially never
74             // succeed in the face of significant updates.
75             lock (m_SyncTable.SyncRoot) {
76                 foreach (object o in m_SyncTable.Keys){
77                     string s = (string) o;
78                     if(s != null && s.Length > lastLength){
79                         if(String.Compare(s,0,canonicalKey,0,s.Length,StringComparison.OrdinalIgnoreCase) == 0){
80                              lastLength = s.Length;
81                              key = s;
82                         }
83                     }
84                 }  
85             }
86             return (key != null) ? (SpnToken)m_SyncTable[key] : null;
87         }
88
89         internal void InternalSet(string canonicalKey, SpnToken spnToken)
90         {
91             m_SyncTable[canonicalKey] = spnToken;
92         }
93         //
94         // Public lookup method
95         //
96         public override string this[string key] {
97             get {
98                 key = GetCanonicalKey(key);
99                 SpnToken token = InternalGet(key);
100                 return (token == null ? null : token.Spn);
101             }
102             set {
103                 key = GetCanonicalKey(key);
104                 // Value may be null
105                 InternalSet(key, new SpnToken(value));
106             }
107         }
108         //
109         public override ICollection Keys {
110             get {
111 #if !DISABLE_CAS_USE
112                 ExceptionHelper.WebPermissionUnrestricted.Demand();
113 #endif
114                 return m_SyncTable.Keys;
115             }
116         }
117         //
118         public override object SyncRoot {
119             [HostProtection(Synchronization=true)]
120             get {
121 #if !DISABLE_CAS_USE
122                 ExceptionHelper.WebPermissionUnrestricted.Demand();
123 #endif
124                 return m_SyncTable;
125             }
126         }
127         //
128         public override ICollection Values {
129             get {
130 #if !DISABLE_CAS_USE
131                 ExceptionHelper.WebPermissionUnrestricted.Demand();
132 #endif
133                 if (m_ValuesWrapper == null)
134                 {
135                     m_ValuesWrapper = new ValueCollection(this);
136                 }
137                 return m_ValuesWrapper;
138             }
139         }
140         //
141         public override void Add(string key, string value) {
142             key = GetCanonicalKey(key);
143             m_SyncTable.Add(key, new SpnToken(value));
144         }
145         //
146         public override void Clear() {
147 #if !DISABLE_CAS_USE
148             ExceptionHelper.WebPermissionUnrestricted.Demand();
149 #endif
150             m_SyncTable.Clear();
151         }
152         //
153         public override bool ContainsKey(string key) {
154             key = GetCanonicalKey(key);
155             return m_SyncTable.ContainsKey(key);
156         }
157         //
158         public override bool ContainsValue(string value) {
159 #if !DISABLE_CAS_USE
160             ExceptionHelper.WebPermissionUnrestricted.Demand();
161 #endif
162             foreach (SpnToken spnToken in m_SyncTable.Values)
163             {
164                 if (spnToken.Spn == value)
165                     return true;
166             }
167             return false;
168         }
169
170         // We have to unwrap the SpnKey and just expose the Spn
171         public override void CopyTo(Array array, int index) {
172 #if !DISABLE_CAS_USE
173             ExceptionHelper.WebPermissionUnrestricted.Demand();
174 #endif
175             CheckCopyToArguments(array, index, Count);
176
177             int offset = 0;
178             foreach (object entry in this)
179             {
180                 array.SetValue(entry, offset + index);
181                 offset++;
182             }
183         }
184         //
185         public override IEnumerator GetEnumerator() {
186 #if !DISABLE_CAS_USE
187             ExceptionHelper.WebPermissionUnrestricted.Demand();
188 #endif
189
190             foreach (string key in m_SyncTable.Keys)
191             {
192                 // We must unwrap the SpnToken and not expose it publicly
193                 SpnToken spnToken = (SpnToken)m_SyncTable[key];
194                 yield return new DictionaryEntry(key, spnToken.Spn);
195             }
196         }
197         //
198         public override void Remove(string key) {
199             key = GetCanonicalKey(key);
200             m_SyncTable.Remove(key);
201         }
202
203         //
204         // Private stuff: We want to serialize on updates on one thread
205         //
206         private static string GetCanonicalKey(string key)
207         {
208             if( key == null ) {
209                 throw new ArgumentNullException("key");
210             }
211             try {
212                 Uri uri = new Uri(key);
213                 key = uri.GetParts(UriComponents.Scheme | UriComponents.Host | UriComponents.Port | UriComponents.Path, UriFormat.SafeUnescaped);
214 #if !DISABLE_CAS_USE
215                 new WebPermission(NetworkAccess.Connect, new Uri(key)).Demand();
216 #endif
217             }
218             catch(UriFormatException e) {
219                 throw new ArgumentException(SR.GetString(SR.net_mustbeuri, "key"), "key", e);
220             }
221             return key;
222         }
223
224         private static void CheckCopyToArguments(Array array, int index, int count)
225         {
226             // Coppied from HashTable.CopyTo
227             if (array == null)
228             {
229                 throw new ArgumentNullException("array");
230             }
231             if (array.Rank != 1)
232             {
233                 throw new ArgumentException(SR.GetString(SR.Arg_RankMultiDimNotSupported));
234             }
235             if (index < 0)
236             {
237                 throw new ArgumentOutOfRangeException("index", SR.GetString(SR.ArgumentOutOfRange_NeedNonNegNum));
238             }
239             if ((array.Length - index) < count)
240             {
241                 throw new ArgumentException(SR.GetString(SR.Arg_ArrayPlusOffTooSmall));
242             }
243         }
244
245         // Wrap HashTable.Values so we can unwrap the SpnTokens
246         private class ValueCollection : ICollection
247         {
248             private SpnDictionary spnDictionary;
249
250             internal ValueCollection(SpnDictionary spnDictionary)
251             {
252                 this.spnDictionary = spnDictionary;
253             }
254
255             public void CopyTo(Array array, int index)
256             {
257                 CheckCopyToArguments(array, index, Count);
258                 
259                 int offset = 0;
260                 foreach (object entry in this)
261                 {
262                     array.SetValue(entry, offset + index);
263                     offset++;
264                 }
265             }
266
267             public int Count
268             {
269                 get { return spnDictionary.m_SyncTable.Values.Count; }
270             }
271
272             public bool IsSynchronized
273             {
274                 get { return true; }
275             }
276
277             public object SyncRoot
278             {
279                 get { return spnDictionary.m_SyncTable.SyncRoot; }
280             }
281
282             public IEnumerator GetEnumerator()
283             {
284                 foreach (SpnToken spnToken in spnDictionary.m_SyncTable.Values)
285                 {
286                     yield return (spnToken != null ? spnToken.Spn : null);
287                 }
288             }
289         }
290     }
291
292     internal class SpnToken
293     {
294         private readonly string spn;
295         private bool isTrusted;
296
297         // Assume the spn is trusted unless a specific reason is found not to trust it.
298         internal bool IsTrusted
299         {
300             get { return isTrusted; }
301             set { isTrusted = false; }
302         }
303
304         internal string Spn
305         {
306             get { return spn; }
307         }
308
309         internal SpnToken(string spn)
310             : this(spn, true)
311         { }
312
313         internal SpnToken(string spn, bool trusted)
314         {
315             this.spn = spn;
316             this.isTrusted = trusted;
317         }
318     }
319 }