2010-06-17 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel.Channels / MessageHeaders.cs
1 //
2 // System.ServiceModel.MessageHeader.cs
3 //
4 // Author: Duncan Mak (duncan@novell.com)
5 //
6 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining
9 // a copy of this software and associated documentation files (the
10 // "Software"), to deal in the Software without restriction, including
11 // without limitation the rights to use, copy, modify, merge, publish,
12 // distribute, sublicense, and/or sell copies of the Software, and to
13 // permit persons to whom the Software is furnished to do so, subject to
14 // the following conditions:
15 // 
16 // The above copyright notice and this permission notice shall be
17 // included in all copies or substantial portions of the Software.
18 // 
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 //
27
28 using System;
29 using System.Collections;
30 using System.Collections.Generic;
31 using System.IO;
32 using System.Runtime.Serialization;
33 using System.ServiceModel;
34 using System.Xml;
35
36 namespace System.ServiceModel.Channels
37 {
38         public sealed class MessageHeaders : IEnumerable<MessageHeaderInfo>, IEnumerable
39         {
40                 static readonly XmlReaderSettings reader_settings;
41
42                 static MessageHeaders ()
43                 {
44                         reader_settings = new XmlReaderSettings ();
45                         reader_settings.ConformanceLevel = ConformanceLevel.Fragment;
46                 }
47
48                 List<MessageHeaderInfo> l;
49                 Dictionary<Type, XmlObjectSerializer> serializers =
50                         new Dictionary<Type, XmlObjectSerializer> ();
51                 MessageVersion version;
52
53                 public MessageHeaders (MessageHeaders headers)
54                         : this (headers.MessageVersion)
55                 {
56                         CopyHeadersFrom (headers);
57                 }
58
59                 public MessageHeaders (MessageVersion version)
60                         : this (version, 10) // let's say 10 is the initial size
61                 {
62                 }
63
64                 public MessageHeaders (MessageVersion version, int capacity)
65                 {
66                         this.version = version;
67                         l = new List<MessageHeaderInfo> (capacity);
68                 }
69                 
70                 public void Add (MessageHeader header)
71                 {
72                         l.Add (header);
73                 }
74
75                 public void CopyHeaderFrom (Message m, int index)
76                 {
77                         CopyHeaderFrom (m.Headers, index);
78                 }
79
80                 public void Clear ()
81                 {
82                         l.Clear ();
83                 }
84
85                 public void CopyHeaderFrom (MessageHeaders headers, int index)
86                 {
87                         l.Add (headers [index]);
88                 }
89
90                 public void CopyHeadersFrom (Message m)
91                 {
92                         CopyHeadersFrom (m.Headers);
93                 }
94
95                 public void CopyHeadersFrom (MessageHeaders headers)
96                 {
97                         foreach (MessageHeaderInfo h in headers)
98                                 l.Add (h);
99                 }
100
101                 public void CopyTo (MessageHeaderInfo [] dst, int index)
102                 {
103                         l.CopyTo (dst, index);
104                 }
105
106                 public int FindHeader (string name, string ns)
107                 {
108                         return FindHeader (name, ns, null);
109                 }
110
111                 bool HasActor (string actor, string [] candidates)
112                 {
113                         foreach (string c in candidates)
114                                 if (c == actor)
115                                         return true;
116                         return false;
117                 }
118
119                 public int FindHeader (string name, string ns, params string [] actors)
120                 {
121                         int found = 0;
122                         int retval = -1;
123                         
124                         for (int i = 0; i < l.Count; i++) {
125                                 MessageHeaderInfo info = l [i];
126
127                                 if (info.Name == name && info.Namespace == ns) {
128                                         if (found > 0)
129                                                 throw new MessageHeaderException ("Found multiple matching headers.");
130                                         // When no actors are passed, it never
131                                         // matches such header that has an
132                                         // Actor.
133                                         if (actors == null && info.Actor == String.Empty ||
134                                             actors != null && HasActor (info.Actor, actors)) {
135                                                 retval = i;
136                                                 found++;
137                                         }
138                                 }
139                         }
140
141                         return retval;
142                 }
143
144                 public IEnumerator<MessageHeaderInfo> GetEnumerator ()
145                 {
146                         return l.GetEnumerator ();
147                 }
148
149                 XmlObjectSerializer GetSerializer<T> (int headerIndex)
150                 {
151                         if (!serializers.ContainsKey (typeof (T)))
152                                 serializers [typeof (T)] = new DataContractSerializer (typeof (T), this [headerIndex].Name, this [headerIndex].Namespace);
153                         return serializers [typeof (T)];
154                 }
155
156                 public T GetHeader<T> (int index)
157                 {
158                         if (l.Count <= index)
159                                 throw new ArgumentOutOfRangeException ("index");
160                         var dmh = l [index] as MessageHeader.DefaultMessageHeader;
161                         if (dmh != null && dmh.Value != null && typeof (T).IsAssignableFrom (dmh.Value.GetType ()))
162                                 return (T) dmh.Value;
163                         if (typeof (T) == typeof (EndpointAddress)) {
164                                 XmlDictionaryReader r = GetReaderAtHeader (index);
165                                 return r.NodeType != XmlNodeType.Element ? default (T) : (T) (object) EndpointAddress.ReadFrom (r);
166                         }
167                         else
168                                 return GetHeader<T> (index, GetSerializer<T> (index));
169                 }
170
171                 public T GetHeader<T> (int index, XmlObjectSerializer serializer)
172                 {
173                         if (serializer == null)
174                                 throw new ArgumentNullException ("serializer");
175                         XmlDictionaryReader r = GetReaderAtHeader (index);
176                         return (T) serializer.ReadObject (r, false);
177                 }
178
179                 public T GetHeader<T> (string name, string ns)
180                 {
181                         return GetHeader<T> (name, ns, (string []) null);
182                 }
183
184                 public T GetHeader<T> (string name, string ns, params string [] actors)
185                 {
186                         int idx = FindHeader (name, ns, actors);
187
188                         if (idx == -1)
189                                 throw new MessageHeaderException (String.Format ("Header '{0}:{1}' was not found for the argument actors: {2}", ns, name, actors == null ? "(null)" : String.Join (",", actors)));
190
191                         return GetHeader<T> (idx);
192                 }
193
194                 public T GetHeader<T> (string name, string ns, XmlObjectSerializer serializer)
195                 {
196                         if (serializer == null)
197                                 throw new ArgumentNullException ("serializer");
198                         int idx = FindHeader (name, ns);
199
200                         if (idx < 0)
201                                 throw new MessageHeaderException (String.Format ("Header '{0}:{1}' was not found", ns, name));
202
203                         return GetHeader<T> (idx, serializer);
204                 }
205
206                 public XmlDictionaryReader GetReaderAtHeader (int index)
207                 {
208                         if (index >= l.Count)
209                                 throw new ArgumentOutOfRangeException (String.Format ("Index is out of range. Current header count is {0}", index));
210                         MessageHeader item = (MessageHeader) l [index];
211
212                         XmlReader reader =
213                                 item is MessageHeader.RawMessageHeader ?
214                                 ((MessageHeader.RawMessageHeader) item).CreateReader () :
215                                 XmlReader.Create (
216                                         new StringReader (item.ToString ()),
217                                         reader_settings);
218                         reader.MoveToContent ();
219                         XmlDictionaryReader dr = XmlDictionaryReader.CreateDictionaryReader (reader);
220                         dr.MoveToContent ();
221                         return dr;
222                 }
223
224                 public bool HaveMandatoryHeadersBeenUnderstood ()
225                 {
226                         throw new NotImplementedException ();
227                 }
228
229                 public bool HaveMandatoryHeadersBeenUnderstood (params string [] actors)
230                 {
231                         throw new NotImplementedException ();
232                 }
233
234                 public void Insert (int index, MessageHeader header)
235                 {
236                         l.Insert (index, header);
237                 }
238
239                 public void RemoveAll (string name, string ns)
240                 {
241                         // Shuffle all the ones we want to keep to the start of the list
242                         int j = 0;
243                         for (int i = 0; i < l.Count; i++) {
244                                 if (l[i].Name != name || l[i].Namespace != ns) {
245                                         l [j++] = l[i];
246                                 }
247                         }
248                         // Trim the extra elements off the end of the list.
249                         int count = l.Count - j;
250                         for (int i = 0; i < count; i++)
251                                 l.RemoveAt (l.Count - 1);
252                 }
253
254                 public void RemoveAt (int index)
255                 {
256                         l.RemoveAt (index);
257                 }
258
259                 IEnumerator IEnumerable.GetEnumerator ()
260                 {
261                         return ((IEnumerable) l).GetEnumerator ();
262                 }
263
264                 public void WriteHeader (int index, XmlDictionaryWriter writer)
265                 {
266                         if (version.Envelope == EnvelopeVersion.None)
267                                 return;
268                         WriteStartHeader (index, writer);
269                         WriteHeaderContents (index, writer);
270                         writer.WriteEndElement ();
271                 }
272
273                 public void WriteHeader (int index, XmlWriter writer)
274                 {
275                         WriteHeader (index, XmlDictionaryWriter.CreateDictionaryWriter (writer));
276                 }
277
278                 public void WriteHeaderContents (int index, XmlDictionaryWriter writer)
279                 {
280                         if (index > l.Count)
281                                 throw new ArgumentOutOfRangeException ("There is no header at position " + index + ".");
282                         
283                         MessageHeader h = l [index] as MessageHeader;
284
285                         h.WriteHeaderContents (writer, version);
286                 }
287
288                 public void WriteHeaderContents (int index, XmlWriter writer)
289                 {
290                         WriteHeaderContents (index, XmlDictionaryWriter.CreateDictionaryWriter (writer));
291                 }
292
293                 public void WriteStartHeader (int index, XmlDictionaryWriter writer)
294                 {
295                         if (index > l.Count)
296                                 throw new ArgumentOutOfRangeException ("There is no header at position " + index + ".");
297
298                         MessageHeader h = l [index] as MessageHeader;
299                         
300                         h.WriteStartHeader (writer, version);
301                 }
302
303                 public void WriteStartHeader (int index, XmlWriter writer)
304                 {
305                         WriteStartHeader (index, XmlDictionaryWriter.CreateDictionaryWriter (writer));
306                 }
307
308                 public string Action {
309                         get {
310                                 int idx = FindHeader ("Action", version.Addressing.Namespace);
311                                 return idx < 0 ? null : GetHeader<string> (idx);
312                         }
313                         set {
314                                 RemoveAll ("Action", version.Addressing.Namespace);
315                                 if (value != null)
316                                         Add (MessageHeader.CreateHeader ("Action", version.Addressing.Namespace, value, true));
317                         }
318                 }
319
320                 public int Count {
321                         get { return l.Count; }
322                 }
323
324                 void AddEndpointAddressHeader (string name, string ns, EndpointAddress address)
325                 {
326                         RemoveAll ("FaultTo", Constants.WsaNamespace);
327                         if (address == null)
328                                 return;
329                         if (MessageVersion.Addressing.Equals (AddressingVersion.WSAddressing10))
330                                 Add (MessageHeader.CreateHeader (name, ns, EndpointAddress10.FromEndpointAddress (address)));
331 #if !NET_2_1
332                         else if (MessageVersion.Addressing.Equals (AddressingVersion.WSAddressingAugust2004))
333                                 Add (MessageHeader.CreateHeader (name, ns, EndpointAddressAugust2004.FromEndpointAddress (address)));
334 #endif
335                         else
336                                 throw new InvalidOperationException ("WS-Addressing header is not allowed for AddressingVersion.None");
337                 }
338
339                 public EndpointAddress FaultTo {
340                         get {
341                                 int idx = FindHeader ("FaultTo", version.Addressing.Namespace);
342                                 return idx < 0 ? null : GetHeader<EndpointAddress> (idx);
343                         }
344                         set {
345                                 RemoveAll ("FaultTo", version.Addressing.Namespace);
346                                 if (value != null)
347                                         AddEndpointAddressHeader ("FaultTo", version.Addressing.Namespace, value);
348                         }
349                 }
350
351                 public EndpointAddress From {
352                         get {
353                                 int idx = FindHeader ("From", version.Addressing.Namespace);
354                                 return idx < 0 ? null : GetHeader<EndpointAddress> (idx);
355                         }
356                         set {
357                                 RemoveAll ("From", version.Addressing.Namespace);
358                                 if (value != null)
359                                         AddEndpointAddressHeader ("From", version.Addressing.Namespace, value);
360                         }
361                 }
362
363                 public MessageHeaderInfo this [int index] {
364                         get { return l [index]; }
365                 }
366
367                 public UniqueId MessageId {
368                         get { 
369                                 int idx = FindHeader ("MessageID", version.Addressing.Namespace);
370                                 return idx < 0 ? null : new UniqueId (GetHeader<string> (idx));
371                         }
372                         set {
373                                 if (version.Addressing == AddressingVersion.None && value != null)
374                                         throw new InvalidOperationException ("WS-Addressing header is not allowed for AddressingVersion.None");
375
376                                 RemoveAll ("MessageID", version.Addressing.Namespace);
377                                 if (value != null)
378                                         Add (MessageHeader.CreateHeader ("MessageID", version.Addressing.Namespace, value));
379                         }
380                 }
381
382                 public MessageVersion MessageVersion { get { return version; } }
383
384                 public UniqueId RelatesTo {
385                         get { 
386                                 int idx = FindHeader ("RelatesTo", version.Addressing.Namespace);
387                                 return idx < 0 ? null : new UniqueId (GetHeader<string> (idx));
388                         }
389                         set {
390                                 if (version.Addressing == AddressingVersion.None && value != null)
391                                         throw new InvalidOperationException ("WS-Addressing header is not allowed for AddressingVersion.None");
392
393                                 RemoveAll ("MessageID", version.Addressing.Namespace);
394                                 if (value != null)
395                                         Add (MessageHeader.CreateHeader ("RelatesTo", version.Addressing.Namespace, value));
396                         }
397
398                 }
399
400                 public EndpointAddress ReplyTo {
401                         get {
402                                 int idx = FindHeader ("ReplyTo", version.Addressing.Namespace);
403                                 return idx < 0 ? null : GetHeader<EndpointAddress> (idx);
404                         }
405                         set {
406                                 RemoveAll ("ReplyTo", version.Addressing.Namespace);
407                                 if (value != null)
408                                         AddEndpointAddressHeader ("ReplyTo", version.Addressing.Namespace, value);
409                         }
410                 }
411
412                 public Uri To {
413                         get {
414                                 int idx = FindHeader ("To", version.Addressing.Namespace);
415                                 //FIXME: return idx < 0 ? null : GetHeader<Uri> (idx);
416                                 return idx < 0 ? null : new Uri (GetHeader<string> (idx));
417                         }
418                         set { 
419                                 RemoveAll ("To", version.Addressing.Namespace);
420                                 if (value != null)
421                                         Add (MessageHeader.CreateHeader ("To", version.Addressing.Namespace, value.AbsoluteUri, true));
422                         }
423                 }
424
425                 [MonoTODO]
426                 public UnderstoodHeaders UnderstoodHeaders {
427                         get { throw new NotImplementedException (); }
428                 }
429
430                 public void SetAction (XmlDictionaryString action)
431                 {
432                         if (action == null)
433                                 Action = null;
434                         else
435                                 Action = action.Value;
436                 }
437         }
438 }