1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //-----------------------------------------------------------------------------
4 namespace System
.ServiceModel
.Dispatcher
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
;
14 using System
.ServiceModel
.Security
;
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
;
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
;
46 this.processor
= (EndpointAddressProcessor
)p
.next
;
53 internal void Push(EndpointAddressProcessor p
)
55 p
.next
= this.processor
;
60 public EndpointAddressMessageFilterTable()
62 this.processorPool
= new WeakReference(null);
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
]
83 return this.filters
[filter
];
87 if (this.filters
.ContainsKey(filter
))
89 this.filters
[filter
] = value;
90 this.candidates
[filter
].data
= value;
94 this.Add(filter
, value);
103 return this.filters
.Count
;
107 public bool IsReadOnly
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
)
135 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperArgumentNull("filter");
138 Add((EndpointAddressMessageFilter
)filter
, data
);
141 public virtual void Add(EndpointAddressMessageFilter filter
, TFilterData data
)
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
);
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
);
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
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;
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
)
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
;
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)
247 this.size
= (this.nextBit
- 1) / 8 + 1;
257 this.filters
.Clear();
258 this.candidates
.Clear();
259 this.headerLookup
.Clear();
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
)
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
;
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
);
333 return this.toNoHostLookup
.TryGetValue(to
, out cset
);
337 Candidate
InnerMatch(Message message
)
339 Uri to
= message
.Headers
.To
;
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
);
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
));
370 Candidate
GetSingleMatch(CandidateSet cset
, Message message
)
372 int candiCount
= cset
.candidates
.Count
;
374 if (cset
.qnames
.Count
== 0)
380 else if (candiCount
== 1)
382 return cset
.candidates
[0];
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
))
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
);
415 ReleaseProcessor(context
);
420 void InnerMatchData(Message message
, ICollection
<TFilterData
> results
)
422 Uri to
= message
.Headers
.To
;
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
;
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
)
492 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperArgumentNull("message");
495 Candidate can
= InnerMatch(message
);
498 data
= default(TFilterData
);
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
);
526 data
= default(TFilterData
);
534 public bool GetMatchingValues(Message message
, ICollection
<TFilterData
> results
)
538 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperArgumentNull("message");
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");
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
;
576 public bool GetMatchingFilter(Message message
, out MessageFilter filter
)
580 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperArgumentNull("message");
583 Candidate can
= InnerMatch(message
);
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
);
622 public bool GetMatchingFilters(Message message
, ICollection
<MessageFilter
> results
)
626 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperArgumentNull("message");
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");
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
;
664 protected void RebuildMasks()
669 // Clear out all the bits.
670 this.headerLookup
.Clear();
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
;
686 pool
= new ProcessorPool();
687 this.processorPool
.Target
= pool
;
689 pool
.Push(processor
);
693 public virtual bool Remove(MessageFilter filter
)
697 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperArgumentNull("filter");
700 EndpointAddressMessageFilter saFilter
= filter
as EndpointAddressMessageFilter
;
701 if (saFilter
!= null)
703 return Remove(saFilter
);
709 public virtual bool Remove(EndpointAddressMessageFilter filter
)
713 throw DiagnosticUtility
.ExceptionUtility
.ThrowHelperArgumentNull("filter");
716 if (!this.filters
.Remove(filter
))
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
];
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
);
745 this.toNoHostLookup
.Remove(soapToAddress
);
750 DecrementQNameCount(cset
, filter
.Address
);
753 cset
.candidates
.Remove(can
);
760 protected void DecrementQNameCount(CandidateSet cset
, EndpointAddress address
)
762 // Adjust QName counts
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
];
773 cset
.qnames
.Remove(qname
);
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
);
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
;
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
);