1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4 namespace System.ServiceModel.Dispatcher
5 {
6     using System.Diagnostics;
7     using System.ServiceModel.Channels;
8     using System.Collections;
9     using System.Collections.Generic;
10     using System.Collections.ObjectModel;
11     using System.Runtime.Serialization;
12     using System.Xml.XPath;
13     using System.ServiceModel.Diagnostics;
14 
15     /// <summary>
16     /// Multi-reader, single writer
17     /// </summary>
18     [DataContract]
19     public class XPathMessageFilterTable<TFilterData> : IMessageFilterTable<TFilterData>
20     {
21         internal Dictionary<MessageFilter, TFilterData> filters;
22         InverseQueryMatcher iqMatcher;  // inverse query matcher
23 
XPathMessageFilterTable()24         public XPathMessageFilterTable()
25         {
26             Init(-1);
27         }
28 
XPathMessageFilterTable(int capacity)29         public XPathMessageFilterTable(int capacity)
30         {
31             if (capacity < 0)
32                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("capacity", capacity, SR.GetString(SR.FilterCapacityNegative)));
33 
34             Init(capacity);
35         }
36 
37         [OnDeserializing]
OnDeserializing(StreamingContext context)38         private void OnDeserializing(StreamingContext context)
39         {
40             Init(-1);
41         }
42 
Init(int capacity)43         void Init(int capacity)
44         {
45             if (capacity <= 0)
46                 this.filters = new Dictionary<MessageFilter, TFilterData>();
47             else
48                 this.filters = new Dictionary<MessageFilter, TFilterData>(capacity);
49 
50             if (this.iqMatcher == null)
51                 this.iqMatcher = new InverseQueryMatcher(true);
52         }
53 
54         bool CanMatch
55         {
56             get
57             {
58                 return (this.filters.Count > 0 && null != this.iqMatcher);
59             }
60         }
61 
62         public TFilterData this[MessageFilter filter]
63         {
64             get
65             {
66                 return this.filters[filter];
67             }
68             set
69             {
70                 if (this.filters.ContainsKey(filter))
71                 {
72                     this.filters[filter] = value;
73                 }
74                 else
75                 {
76                     this.Add(filter, value);
77                 }
78             }
79         }
80 
81         public int Count
82         {
83             get
84             {
85                 return this.filters.Count;
86             }
87         }
88 
89         [DataMember]
90         Entry[] Entries
91         {
92             get
93             {
94                 Entry[] entries = new Entry[Count];
95                 int i = 0;
96                 foreach (KeyValuePair<MessageFilter, TFilterData> item in filters)
97                     entries[i++] = new Entry(item.Key, item.Value);
98 
99                 return entries;
100             }
101             set
102             {
103                 Init(value.Length);
104 
105                 for (int i = 0; i < value.Length; ++i)
106                     Add(value[i].filter, value[i].data);
107             }
108         }
109 
110         public bool IsReadOnly
111         {
112             get
113             {
114                 return false;
115             }
116         }
117 
118         public ICollection<MessageFilter> Keys
119         {
120             get
121             {
122                 return this.filters.Keys;
123             }
124         }
125 
126         /// <summary>
127         /// Some filters could be extremely expensive to evaluate or are very long running (infinite). A
128         /// filter table could have a very large number of relatively simple filters that taken as a whole would have
129         /// a very long running time. XPathFilters could be created using XPath off the wire, which may be malicious.
130         /// Since filters operate on Xml infosets, a natural and simple way to set computational limits on filter tables
131         /// is to specify the maximum # of nodes that should be looked at while evaluating ANY of the filters in this
132         /// table.
133         /// </summary>
134         [DataMember]
135         public int NodeQuota
136         {
137             get
138             {
139                 //return (null == this.iqMatcher) ? int.MaxValue : this.iqMatcher.NodeQuota;
140                 return this.iqMatcher.NodeQuota;
141             }
142             set
143             {
144                 if (value <= 0)
145                 {
146                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("NodeQuota", value, SR.GetString(SR.FilterQuotaRange)));
147                 }
148 
149                 if (null == this.iqMatcher)
150                 {
151                     this.iqMatcher = new InverseQueryMatcher(true);
152                 }
153                 this.iqMatcher.NodeQuota = value;
154             }
155         }
156 
157         public ICollection<TFilterData> Values
158         {
159             get
160             {
161                 return this.filters.Values;
162             }
163         }
164 
Add(MessageFilter filter, TFilterData data)165         public void Add(MessageFilter filter, TFilterData data)
166         {
167             this.Add((XPathMessageFilter)filter, data);
168         }
169 
Add(KeyValuePair<MessageFilter, TFilterData> item)170         public void Add(KeyValuePair<MessageFilter, TFilterData> item)
171         {
172             this.Add(item.Key, item.Value);
173         }
174 
Add(XPathMessageFilter filter, TFilterData data)175         public void Add(XPathMessageFilter filter, TFilterData data)
176         {
177             this.Add(filter, data, false);
178         }
179 
Add(XPathMessageFilter filter, TFilterData data, bool forceExternal)180         internal void Add(XPathMessageFilter filter, TFilterData data, bool forceExternal)
181         {
182             if (null == filter)
183             {
184                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter");
185             }
186 
187             //this.EnsureMatcher();
188             this.filters.Add(filter, data);
189             this.iqMatcher.Add(filter.XPath, filter.Namespaces, filter, forceExternal);
190         }
191 
Clear()192         public void Clear()
193         {
194             this.iqMatcher.Clear();
195             this.filters.Clear();
196         }
197 
Contains(KeyValuePair<MessageFilter, TFilterData> item)198         public bool Contains(KeyValuePair<MessageFilter, TFilterData> item)
199         {
200             return ((IDictionary<MessageFilter, TFilterData>)this.filters).Contains(item);
201         }
202 
ContainsKey(MessageFilter filter)203         public bool ContainsKey(MessageFilter filter)
204         {
205             return this.filters.ContainsKey(filter);
206         }
207 
CopyTo(KeyValuePair<MessageFilter, TFilterData>[] array, int arrayIndex)208         public void CopyTo(KeyValuePair<MessageFilter, TFilterData>[] array, int arrayIndex)
209         {
210             ((IDictionary<MessageFilter, TFilterData>)this.filters).CopyTo(array, arrayIndex);
211         }
212 
213 #if NO
EnsureMatcher()214         void EnsureMatcher()
215         {
216             if (null == this.iqMatcher)
217             {
218                 this.iqMatcher = new InverseQueryMatcher();
219             }
220         }
221 #endif
222 
IEnumerable.GetEnumerator()223         IEnumerator IEnumerable.GetEnumerator()
224         {
225             return this.GetEnumerator();
226         }
227 
GetEnumerator()228         public IEnumerator<KeyValuePair<MessageFilter, TFilterData>> GetEnumerator()
229         {
230             return ((IDictionary<MessageFilter, TFilterData>)this.filters).GetEnumerator();
231         }
232 
GetMatchingValue(Message message, out TFilterData data)233         public bool GetMatchingValue(Message message, out TFilterData data)
234         {
235             if (null == message)
236             {
237                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message");
238             }
239 
240             if (this.CanMatch)
241             {
242                 return this.ProcessMatch(this.iqMatcher.Match(message, false, null), out data);
243             }
244 
245             data = default(TFilterData);
246             return false;
247         }
248 
GetMatchingValue(MessageBuffer messageBuffer, out TFilterData data)249         public bool GetMatchingValue(MessageBuffer messageBuffer, out TFilterData data)
250         {
251             if (null == messageBuffer)
252             {
253                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageBuffer");
254             }
255 
256             if (this.CanMatch)
257             {
258                 return this.ProcessMatch(this.iqMatcher.Match(messageBuffer, null), out data);
259             }
260 
261             data = default(TFilterData);
262             return false;
263         }
264 
GetMatchingValue(SeekableXPathNavigator navigator, out TFilterData data)265         public bool GetMatchingValue(SeekableXPathNavigator navigator, out TFilterData data)
266         {
267             if (null == navigator)
268             {
269                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("navigator");
270             }
271 
272             if (this.CanMatch)
273             {
274                 return this.ProcessMatch(this.iqMatcher.Match(navigator, null), out data);
275             }
276 
277             data = default(TFilterData);
278             return false;
279         }
280 
GetMatchingValue(XPathNavigator navigator, out TFilterData data)281         public bool GetMatchingValue(XPathNavigator navigator, out TFilterData data)
282         {
283             if (null == navigator)
284             {
285                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("navigator");
286             }
287 
288             if (this.CanMatch)
289             {
290                 return this.ProcessMatch(this.iqMatcher.Match(navigator, null), out data);
291             }
292 
293             data = default(TFilterData);
294             return false;
295         }
296 
GetMatchingFilter(Message message, out MessageFilter filter)297         public bool GetMatchingFilter(Message message, out MessageFilter filter)
298         {
299             Collection<MessageFilter> filters = new Collection<MessageFilter>();
300             this.GetMatchingFilters(message, filters);
301             if (filters.Count > 1)
302             {
303                 throw TraceUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.GetString(SR.FilterMultipleMatches), null, filters), message);
304             }
305             else if (filters.Count == 1)
306             {
307                 filter = filters[0];
308                 return true;
309             }
310             else
311             {
312                 filter = null;
313                 return false;
314             }
315         }
316 
GetMatchingFilter(MessageBuffer messageBuffer, out MessageFilter filter)317         public bool GetMatchingFilter(MessageBuffer messageBuffer, out MessageFilter filter)
318         {
319             Collection<MessageFilter> filters = new Collection<MessageFilter>();
320             this.GetMatchingFilters(messageBuffer, filters);
321             if (filters.Count > 1)
322             {
323                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.GetString(SR.FilterMultipleMatches), null, filters));
324             }
325             else if (filters.Count == 1)
326             {
327                 filter = filters[0];
328                 return true;
329             }
330             else
331             {
332                 filter = null;
333                 return false;
334             }
335         }
336 
GetMatchingFilter(SeekableXPathNavigator navigator, out MessageFilter filter)337         public bool GetMatchingFilter(SeekableXPathNavigator navigator, out MessageFilter filter)
338         {
339             Collection<MessageFilter> filters = new Collection<MessageFilter>();
340             this.GetMatchingFilters(navigator, filters);
341             if (filters.Count > 1)
342             {
343                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.GetString(SR.FilterMultipleMatches), null, filters));
344             }
345             else if (filters.Count == 1)
346             {
347                 filter = filters[0];
348                 return true;
349             }
350             else
351             {
352                 filter = null;
353                 return false;
354             }
355         }
356 
GetMatchingFilter(XPathNavigator navigator, out MessageFilter filter)357         public bool GetMatchingFilter(XPathNavigator navigator, out MessageFilter filter)
358         {
359             Collection<MessageFilter> filters = new Collection<MessageFilter>();
360             this.GetMatchingFilters(navigator, filters);
361             if (filters.Count > 1)
362             {
363                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MultipleFilterMatchesException(SR.GetString(SR.FilterMultipleMatches), null, filters));
364             }
365             else if (filters.Count == 1)
366             {
367                 filter = filters[0];
368                 return true;
369             }
370             else
371             {
372                 filter = null;
373                 return false;
374             }
375         }
376 
GetMatchingFilters(Message message, ICollection<MessageFilter> results)377         public bool GetMatchingFilters(Message message, ICollection<MessageFilter> results)
378         {
379             if (null == message)
380             {
381                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message");
382             }
383             if (null == results)
384             {
385                 throw TraceUtility.ThrowHelperArgumentNull("results", message);
386             }
387 
388             if (this.CanMatch)
389             {
390                 int count = results.Count;
391                 this.iqMatcher.ReleaseResult(this.iqMatcher.Match(message, false, results));
392                 return count != results.Count;
393             }
394             return false;
395         }
396 
GetMatchingFilters(MessageBuffer messageBuffer, ICollection<MessageFilter> results)397         public bool GetMatchingFilters(MessageBuffer messageBuffer, ICollection<MessageFilter> results)
398         {
399             if (null == messageBuffer)
400             {
401                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageBuffer");
402             }
403             if (null == results)
404             {
405                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results");
406             }
407 
408             if (this.CanMatch)
409             {
410                 int count = results.Count;
411                 this.iqMatcher.ReleaseResult(iqMatcher.Match(messageBuffer, results));
412                 return count != results.Count;
413             }
414             return false;
415         }
416 
GetMatchingFilters(SeekableXPathNavigator navigator, ICollection<MessageFilter> results)417         public bool GetMatchingFilters(SeekableXPathNavigator navigator, ICollection<MessageFilter> results)
418         {
419             if (null == navigator)
420             {
421                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("navigator");
422             }
423             if (null == results)
424             {
425                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results");
426             }
427 
428             if (this.CanMatch)
429             {
430                 int count = results.Count;
431                 this.iqMatcher.ReleaseResult(this.iqMatcher.Match(navigator, results));
432                 return count != results.Count;
433             }
434             return false;
435         }
436 
GetMatchingFilters(XPathNavigator navigator, ICollection<MessageFilter> results)437         public bool GetMatchingFilters(XPathNavigator navigator, ICollection<MessageFilter> results)
438         {
439             if (null == navigator)
440             {
441                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("navigator");
442             }
443             if (null == results)
444             {
445                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results");
446             }
447 
448             if (this.CanMatch)
449             {
450                 int count = results.Count;
451                 this.iqMatcher.ReleaseResult(this.iqMatcher.Match(navigator, results));
452                 return count != results.Count;
453             }
454             return false;
455         }
456 
GetMatchingValues(Message message, ICollection<TFilterData> results)457         public bool GetMatchingValues(Message message, ICollection<TFilterData> results)
458         {
459             if (null == message)
460             {
461                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message");
462             }
463             if (null == results)
464             {
465                 throw TraceUtility.ThrowHelperArgumentNull("results", message);
466             }
467 
468             if (this.CanMatch)
469             {
470                 int count = results.Count;
471                 this.ProcessMatches(this.iqMatcher.Match(message, false, null), results);
472                 return count != results.Count;
473             }
474             return false;
475         }
476 
GetMatchingValues(MessageBuffer messageBuffer, ICollection<TFilterData> results)477         public bool GetMatchingValues(MessageBuffer messageBuffer, ICollection<TFilterData> results)
478         {
479             if (null == messageBuffer)
480             {
481                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageBuffer");
482             }
483             if (null == results)
484             {
485                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results");
486             }
487 
488             if (this.CanMatch)
489             {
490                 int count = results.Count;
491                 this.ProcessMatches(this.iqMatcher.Match(messageBuffer, null), results);
492                 return count != results.Count;
493             }
494             return false;
495         }
496 
GetMatchingValues(SeekableXPathNavigator navigator, ICollection<TFilterData> results)497         public bool GetMatchingValues(SeekableXPathNavigator navigator, ICollection<TFilterData> results)
498         {
499             if (null == navigator)
500             {
501                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("navigator");
502             }
503             if (null == results)
504             {
505                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results");
506             }
507 
508             if (this.CanMatch)
509             {
510                 int count = results.Count;
511                 this.ProcessMatches(this.iqMatcher.Match(navigator, null), results);
512                 return count != results.Count;
513             }
514             return false;
515         }
516 
GetMatchingValues(XPathNavigator navigator, ICollection<TFilterData> results)517         public bool GetMatchingValues(XPathNavigator navigator, ICollection<TFilterData> results)
518         {
519             if (null == navigator)
520             {
521                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("navigator");
522             }
523             if (null == results)
524             {
525                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("results");
526             }
527 
528             if (this.CanMatch)
529             {
530                 int count = results.Count;
531                 this.ProcessMatches(this.iqMatcher.Match(navigator, null), results);
532                 return count != results.Count;
533             }
534             return false;
535         }
536 
ProcessMatch(FilterResult result, out TFilterData data)537         bool ProcessMatch(FilterResult result, out TFilterData data)
538         {
539             bool retVal = false;
540             data = default(TFilterData);
541             MessageFilter match = result.GetSingleMatch();
542             if (null != match)
543             {
544                 data = this.filters[match];
545                 retVal = true;
546             }
547             this.iqMatcher.ReleaseResult(result);
548             return retVal;
549         }
550 
ProcessMatches(FilterResult result, ICollection<TFilterData> results)551         void ProcessMatches(FilterResult result, ICollection<TFilterData> results)
552         {
553             Collection<MessageFilter> matches = result.Processor.MatchList;
554             for (int i = 0, count = matches.Count; i < count; ++i)
555             {
556                 results.Add(this.filters[matches[i]]);
557             }
558             this.iqMatcher.ReleaseResult(result);
559         }
560 
Remove(MessageFilter filter)561         public bool Remove(MessageFilter filter)
562         {
563             if (null == filter)
564             {
565                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter");
566             }
567 
568             XPathMessageFilter xpf = filter as XPathMessageFilter;
569             if (xpf != null)
570             {
571                 return this.Remove(xpf);
572             }
573             return false;
574         }
575 
Remove(KeyValuePair<MessageFilter, TFilterData> item)576         public bool Remove(KeyValuePair<MessageFilter, TFilterData> item)
577         {
578             if (((IDictionary<MessageFilter, TFilterData>)this.filters).Remove(item))
579             {
580                 this.iqMatcher.Remove((XPathMessageFilter)item.Key);
581                 return true;
582             }
583 
584             return false;
585         }
586 
Remove(XPathMessageFilter filter)587         public bool Remove(XPathMessageFilter filter)
588         {
589             if (null == filter)
590             {
591                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("filter");
592             }
593 
594             if (this.filters.Remove(filter))
595             {
596                 this.iqMatcher.Remove(filter);
597                 return true;
598             }
599 
600             // Not in this table
601             return false;
602         }
603 
TrimToSize()604         public void TrimToSize()
605         {
606             this.iqMatcher.Trim();
607         }
608 
TryGetValue(MessageFilter filter, out TFilterData data)609         public bool TryGetValue(MessageFilter filter, out TFilterData data)
610         {
611             return this.filters.TryGetValue(filter, out data);
612         }
613 
614         [DataContract]
615         class Entry
616         {
617             [DataMember(IsRequired = true)]
618             internal MessageFilter filter;
619 
620             [DataMember(IsRequired = true)]
621             internal TFilterData data;
622 
Entry(MessageFilter f, TFilterData d)623             internal Entry(MessageFilter f, TFilterData d)
624             {
625                 filter = f;
626                 data = d;
627             }
628         }
629     }
630 }
631