Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.ServiceModel / System / ServiceModel / Dispatcher / EndpointAddressMessageFilterTable.cs
blob7f5e509b82b28247923011b76c2740d2e2091194
1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //-----------------------------------------------------------------------------
4 namespace System.ServiceModel.Dispatcher
6 using System;
7 using System.ServiceModel.Channels;
8 using System.ServiceModel;
9 using System.Collections;
10 using System.Collections.Generic;
11 using System.Collections.ObjectModel;
12 using System.Diagnostics;
13 using System.IO;
14 using System.ServiceModel.Security;
15 using System.Xml;
17 using QName = System.ServiceModel.Dispatcher.EndpointAddressProcessor.QName;
18 using HeaderBit = System.ServiceModel.Dispatcher.EndpointAddressProcessor.HeaderBit;
19 using System.ServiceModel.Diagnostics;
21 internal class EndpointAddressMessageFilterTable<TFilterData> : IMessageFilterTable<TFilterData>
23 protected Dictionary<MessageFilter, TFilterData> filters;
24 protected Dictionary<MessageFilter, Candidate> candidates;
25 WeakReference processorPool;
27 int size;
28 int nextBit;
29 Dictionary<string, HeaderBit[]> headerLookup;
30 Dictionary<Uri, CandidateSet> toHostLookup;
31 Dictionary<Uri, CandidateSet> toNoHostLookup;
33 internal class ProcessorPool
35 EndpointAddressProcessor processor;
37 internal ProcessorPool()
41 internal EndpointAddressProcessor Pop()
43 EndpointAddressProcessor p = this.processor;
44 if (null != p)
46 this.processor = (EndpointAddressProcessor)p.next;
47 p.next = null;
48 return p;
50 return null;
53 internal void Push(EndpointAddressProcessor p)
55 p.next = this.processor;
56 this.processor = p;
60 public EndpointAddressMessageFilterTable()
62 this.processorPool = new WeakReference(null);
64 this.size = 0;
65 this.nextBit = 0;
67 this.filters = new Dictionary<MessageFilter, TFilterData>();
68 this.candidates = new Dictionary<MessageFilter, Candidate>();
69 this.headerLookup = new Dictionary<string, HeaderBit[]>();
70 InitializeLookupTables();
73 protected virtual void InitializeLookupTables()
75 this.toHostLookup = new Dictionary<Uri, CandidateSet>(EndpointAddressMessageFilter.HostUriComparer.Value);
76 this.toNoHostLookup = new Dictionary<Uri, CandidateSet>(EndpointAddressMessageFilter.NoHostUriComparer.Value);
79 public TFilterData this[MessageFilter filter]
81 get
83 return this.filters[filter];
85 set
87 if (this.filters.ContainsKey(filter))
89 this.filters[filter] = value;
90 this.candidates[filter].data = value;
92 else
94 this.Add(filter, value);
99 public int Count
103 return this.filters.Count;
107 public bool IsReadOnly
111 return false;
115 public ICollection<MessageFilter> Keys
119 return this.filters.Keys;
123 public ICollection<TFilterData> Values
127 return this.filters.Values;
131 public virtual void Add(MessageFilter filter, TFilterData data)
133 if (filter == null)
135 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter");
138 Add((EndpointAddressMessageFilter)filter, data);
141 public virtual void Add(EndpointAddressMessageFilter filter, TFilterData data)
143 if (filter == null)
145 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter");
148 this.filters.Add(filter, data);
150 // Create the candidate
151 byte[] mask = BuildMask(filter.HeaderLookup);
152 Candidate can = new Candidate(filter, data, mask, filter.HeaderLookup);
153 this.candidates.Add(filter, can);
155 CandidateSet cset;
156 #pragma warning suppress 56506 // Microsoft, EndpointAddressMessageFilter.Address can never be null
157 Uri soapToAddress = filter.Address.Uri;
158 if (filter.IncludeHostNameInComparison)
160 if (!this.toHostLookup.TryGetValue(soapToAddress, out cset))
162 cset = new CandidateSet();
163 this.toHostLookup.Add(soapToAddress, cset);
166 else
168 if (!this.toNoHostLookup.TryGetValue(soapToAddress, out cset))
170 cset = new CandidateSet();
171 this.toNoHostLookup.Add(soapToAddress, cset);
174 cset.candidates.Add(can);
176 IncrementQNameCount(cset, filter.Address);
179 protected void IncrementQNameCount(CandidateSet cset, EndpointAddress address)
181 // Update the QName ref count
182 QName qname;
183 int cnt;
184 #pragma warning suppress 56506 // Microsoft, EndpointAddressMessageFilter.Address can never be null
185 for (int i = 0; i < address.Headers.Count; ++i)
187 AddressHeader parameter = address.Headers[i];
188 qname.name = parameter.Name;
189 qname.ns = parameter.Namespace;
190 if (cset.qnames.TryGetValue(qname, out cnt))
192 cset.qnames[qname] = cnt + 1;
194 else
196 cset.qnames.Add(qname, 1);
201 public void Add(KeyValuePair<MessageFilter, TFilterData> item)
203 Add(item.Key, item.Value);
206 protected byte[] BuildMask(Dictionary<string, HeaderBit[]> headerLookup)
208 HeaderBit[] bits;
209 byte[] mask = null;
210 foreach (KeyValuePair<string, HeaderBit[]> item in headerLookup)
212 if (this.headerLookup.TryGetValue(item.Key, out bits))
214 if (bits.Length < item.Value.Length)
216 int old = bits.Length;
217 Array.Resize(ref bits, item.Value.Length);
218 for (int i = old; i < item.Value.Length; ++i)
220 bits[i] = new HeaderBit(this.nextBit++);
222 this.headerLookup[item.Key] = bits;
225 else
227 bits = new HeaderBit[item.Value.Length];
228 for (int i = 0; i < item.Value.Length; ++i)
230 bits[i] = new HeaderBit(this.nextBit++);
232 this.headerLookup.Add(item.Key, bits);
235 for (int i = 0; i < item.Value.Length; ++i)
237 bits[i].AddToMask(ref mask);
241 if (this.nextBit == 0)
243 this.size = 0;
245 else
247 this.size = (this.nextBit - 1) / 8 + 1;
250 return mask;
253 public void Clear()
255 this.size = 0;
256 this.nextBit = 0;
257 this.filters.Clear();
258 this.candidates.Clear();
259 this.headerLookup.Clear();
260 ClearLookupTables();
263 protected virtual void ClearLookupTables()
265 if (this.toHostLookup != null)
267 this.toHostLookup.Clear();
269 if (this.toNoHostLookup != null)
271 this.toNoHostLookup.Clear();
275 public bool Contains(KeyValuePair<MessageFilter, TFilterData> item)
277 return ((ICollection<KeyValuePair<MessageFilter, TFilterData>>)this.filters).Contains(item);
280 public bool ContainsKey(MessageFilter filter)
282 if (filter == null)
284 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter");
286 return this.filters.ContainsKey(filter);
289 public void CopyTo(KeyValuePair<MessageFilter, TFilterData>[] array, int arrayIndex)
291 ((ICollection<KeyValuePair<MessageFilter, TFilterData>>)this.filters).CopyTo(array, arrayIndex);
294 EndpointAddressProcessor CreateProcessor(int length)
296 EndpointAddressProcessor p = null;
297 lock (this.processorPool)
299 ProcessorPool pool = this.processorPool.Target as ProcessorPool;
300 if (null != pool)
302 p = pool.Pop();
306 if (null != p)
308 p.Clear(length);
309 return p;
312 return new EndpointAddressProcessor(length);
315 IEnumerator IEnumerable.GetEnumerator()
317 return this.GetEnumerator();
320 public IEnumerator<KeyValuePair<MessageFilter, TFilterData>> GetEnumerator()
322 return this.filters.GetEnumerator();
325 internal virtual bool TryMatchCandidateSet(Uri to, bool includeHostNameInComparison, out CandidateSet cset)
327 if (includeHostNameInComparison)
329 return this.toHostLookup.TryGetValue(to, out cset);
331 else
333 return this.toNoHostLookup.TryGetValue(to, out cset);
337 Candidate InnerMatch(Message message)
339 Uri to = message.Headers.To;
340 if (to == null)
342 return null;
345 CandidateSet cset = null;
346 Candidate can = null;
347 if (TryMatchCandidateSet(to, true/*includeHostNameInComparison*/, out cset))
349 can = GetSingleMatch(cset, message);
351 if (TryMatchCandidateSet(to, false/*includeHostNameInComparison*/, out cset))
353 Candidate c = GetSingleMatch(cset, message);
354 if (c != null)
356 if (can != null)
358 Collection<MessageFilter> matches = new Collection<MessageFilter>();
359 matches.Add(can.filter);
360 matches.Add(c.filter);
361 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.GetString(SR.FilterMultipleMatches), null, matches));
363 can = c;
367 return can;
370 Candidate GetSingleMatch(CandidateSet cset, Message message)
372 int candiCount = cset.candidates.Count;
374 if (cset.qnames.Count == 0)
376 if (candiCount == 0)
378 return null;
380 else if (candiCount == 1)
382 return cset.candidates[0];
384 else
386 Collection<MessageFilter> matches = new Collection<MessageFilter>();
387 for (int i = 0; i < candiCount; ++i)
389 matches.Add(cset.candidates[i].filter);
391 throw TraceUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.GetString(SR.FilterMultipleMatches), null, matches), message);
395 EndpointAddressProcessor context = CreateProcessor(this.size);
396 context.ProcessHeaders(message, cset.qnames, this.headerLookup);
398 Candidate can = null;
399 List<Candidate> candis = cset.candidates;
400 for (int i = 0; i < candiCount; ++i)
402 if (context.TestMask(candis[i].mask))
404 if (can != null)
406 Collection<MessageFilter> matches = new Collection<MessageFilter>();
407 matches.Add(can.filter);
408 matches.Add(candis[i].filter);
409 throw TraceUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.GetString(SR.FilterMultipleMatches), null, matches), message);
411 can = candis[i];
415 ReleaseProcessor(context);
417 return can;
420 void InnerMatchData(Message message, ICollection<TFilterData> results)
422 Uri to = message.Headers.To;
423 if (to != null)
425 CandidateSet cset;
426 if (TryMatchCandidateSet(to, true /*includeHostNameInComparison*/, out cset))
428 InnerMatchData(message, results, cset);
430 if (TryMatchCandidateSet(to, false /*includeHostNameInComparison*/, out cset))
432 InnerMatchData(message, results, cset);
437 void InnerMatchData(Message message, ICollection<TFilterData> results, CandidateSet cset)
439 EndpointAddressProcessor context = CreateProcessor(this.size);
440 context.ProcessHeaders(message, cset.qnames, this.headerLookup);
442 List<Candidate> candis = cset.candidates;
443 for (int i = 0; i < candis.Count; ++i)
445 if (context.TestMask(candis[i].mask))
447 results.Add(candis[i].data);
451 ReleaseProcessor(context);
454 protected void InnerMatchFilters(Message message, ICollection<MessageFilter> results)
456 Uri to = message.Headers.To;
457 if (to != null)
459 CandidateSet cset;
460 if (TryMatchCandidateSet(to, true/*includeHostNameInComparison*/, out cset))
462 InnerMatchFilters(message, results, cset);
464 if (TryMatchCandidateSet(to, false/*includeHostNameInComparison*/, out cset))
466 InnerMatchFilters(message, results, cset);
471 void InnerMatchFilters(Message message, ICollection<MessageFilter> results, CandidateSet cset)
473 EndpointAddressProcessor context = CreateProcessor(this.size);
474 context.ProcessHeaders(message, cset.qnames, this.headerLookup);
476 List<Candidate> candis = cset.candidates;
477 for (int i = 0; i < candis.Count; ++i)
479 if (context.TestMask(candis[i].mask))
481 results.Add(candis[i].filter);
485 ReleaseProcessor(context);
488 public bool GetMatchingValue(Message message, out TFilterData data)
490 if (message == null)
492 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message");
495 Candidate can = InnerMatch(message);
496 if (can == null)
498 data = default(TFilterData);
499 return false;
502 data = can.data;
503 return true;
506 public bool GetMatchingValue(MessageBuffer messageBuffer, out TFilterData data)
508 if (messageBuffer == null)
510 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageBuffer");
513 Message msg = messageBuffer.CreateMessage();
514 Candidate can = null;
517 can = InnerMatch(msg);
519 finally
521 msg.Close();
524 if (can == null)
526 data = default(TFilterData);
527 return false;
530 data = can.data;
531 return true;
534 public bool GetMatchingValues(Message message, ICollection<TFilterData> results)
536 if (message == null)
538 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message");
541 if (results == null)
543 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results");
546 int count = results.Count;
547 InnerMatchData(message, results);
548 return count != results.Count;
551 public bool GetMatchingValues(MessageBuffer messageBuffer, ICollection<TFilterData> results)
553 if (messageBuffer == null)
555 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageBuffer");
558 if (results == null)
560 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results");
563 Message msg = messageBuffer.CreateMessage();
566 int count = results.Count;
567 InnerMatchData(msg, results);
568 return count != results.Count;
570 finally
572 msg.Close();
576 public bool GetMatchingFilter(Message message, out MessageFilter filter)
578 if (message == null)
580 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message");
583 Candidate can = InnerMatch(message);
584 if (can != null)
586 filter = can.filter;
587 return true;
590 filter = null;
591 return false;
594 public bool GetMatchingFilter(MessageBuffer messageBuffer, out MessageFilter filter)
596 if (messageBuffer == null)
598 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageBuffer");
601 Message msg = messageBuffer.CreateMessage();
602 Candidate can = null;
605 can = InnerMatch(msg);
607 finally
609 msg.Close();
612 if (can != null)
614 filter = can.filter;
615 return true;
618 filter = null;
619 return false;
622 public bool GetMatchingFilters(Message message, ICollection<MessageFilter> results)
624 if (message == null)
626 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message");
629 if (results == null)
631 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results");
634 int count = results.Count;
635 InnerMatchFilters(message, results);
636 return count != results.Count;
639 public bool GetMatchingFilters(MessageBuffer messageBuffer, ICollection<MessageFilter> results)
641 if (messageBuffer == null)
643 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageBuffer");
646 if (results == null)
648 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results");
651 Message msg = messageBuffer.CreateMessage();
654 int count = results.Count;
655 InnerMatchFilters(msg, results);
656 return count != results.Count;
658 finally
660 msg.Close();
664 protected void RebuildMasks()
666 this.nextBit = 0;
667 this.size = 0;
669 // Clear out all the bits.
670 this.headerLookup.Clear();
672 // Rebuild the masks
673 foreach (Candidate can in this.candidates.Values)
675 can.mask = BuildMask(can.headerLookup);
679 void ReleaseProcessor(EndpointAddressProcessor processor)
681 lock (this.processorPool)
683 ProcessorPool pool = this.processorPool.Target as ProcessorPool;
684 if (null == pool)
686 pool = new ProcessorPool();
687 this.processorPool.Target = pool;
689 pool.Push(processor);
693 public virtual bool Remove(MessageFilter filter)
695 if (filter == null)
697 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter");
700 EndpointAddressMessageFilter saFilter = filter as EndpointAddressMessageFilter;
701 if (saFilter != null)
703 return Remove(saFilter);
706 return false;
709 public virtual bool Remove(EndpointAddressMessageFilter filter)
711 if (filter == null)
713 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter");
716 if (!this.filters.Remove(filter))
718 return false;
721 Candidate can = this.candidates[filter];
722 #pragma warning suppress 56506 // Microsoft, EndpointAddressMessageFilter.Address can never be null
723 Uri soapToAddress = filter.Address.Uri;
725 CandidateSet cset = null;
726 if (filter.IncludeHostNameInComparison)
728 cset = this.toHostLookup[soapToAddress];
730 else
732 cset = this.toNoHostLookup[soapToAddress];
735 this.candidates.Remove(filter);
737 if (cset.candidates.Count == 1)
739 if (filter.IncludeHostNameInComparison)
741 this.toHostLookup.Remove(soapToAddress);
743 else
745 this.toNoHostLookup.Remove(soapToAddress);
748 else
750 DecrementQNameCount(cset, filter.Address);
752 // Remove Candidate
753 cset.candidates.Remove(can);
756 RebuildMasks();
757 return true;
760 protected void DecrementQNameCount(CandidateSet cset, EndpointAddress address)
762 // Adjust QName counts
763 QName qname;
764 #pragma warning suppress 56506 // Microsoft, EndpointAddress.Headers can never be null
765 for (int i = 0; i < address.Headers.Count; ++i)
767 AddressHeader parameter = address.Headers[i];
768 qname.name = parameter.Name;
769 qname.ns = parameter.Namespace;
770 int cnt = cset.qnames[qname];
771 if (cnt == 1)
773 cset.qnames.Remove(qname);
775 else
777 cset.qnames[qname] = cnt - 1;
782 public bool Remove(KeyValuePair<MessageFilter, TFilterData> item)
784 if (((ICollection<KeyValuePair<MessageFilter, TFilterData>>)this.filters).Contains(item))
786 return Remove(item.Key);
788 return false;
791 internal class Candidate
793 internal MessageFilter filter;
794 internal TFilterData data;
795 internal byte[] mask;
796 internal Dictionary<string, HeaderBit[]> headerLookup;
798 internal Candidate(MessageFilter filter, TFilterData data, byte[] mask, Dictionary<string, HeaderBit[]> headerLookup)
800 this.filter = filter;
801 this.data = data;
802 this.mask = mask;
803 this.headerLookup = headerLookup;
807 internal class CandidateSet
809 internal Dictionary<QName, int> qnames;
810 internal List<Candidate> candidates;
812 internal CandidateSet()
814 this.qnames = new Dictionary<QName, int>(EndpointAddressProcessor.QNameComparer);
815 this.candidates = new List<Candidate>();
819 public bool TryGetValue(MessageFilter filter, out TFilterData data)
821 return this.filters.TryGetValue(filter, out data);