2010-03-12 Jb Evain <jbevain@novell.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 string [] empty_strings = new string [0];
41
42                 static readonly XmlReaderSettings reader_settings;
43
44                 static MessageHeaders ()
45                 {
46                         reader_settings = new XmlReaderSettings ();
47                         reader_settings.ConformanceLevel = ConformanceLevel.Fragment;
48                 }
49
50                 List<MessageHeaderInfo> l;
51                 Dictionary<Type, XmlObjectSerializer> serializers =
52                         new Dictionary<Type, XmlObjectSerializer> ();
53                 MessageVersion version;
54
55                 public MessageHeaders (MessageHeaders headers)
56                         : this (headers.MessageVersion)
57                 {
58                         CopyHeadersFrom (headers);
59                 }
60
61                 public MessageHeaders (MessageVersion version)
62                         : this (version, 10) // let's say 10 is the initial size
63                 {
64                 }
65
66                 public MessageHeaders (MessageVersion version, int capacity)
67                 {
68                         this.version = version;
69                         l = new List<MessageHeaderInfo> (capacity);
70                 }
71                 
72                 public void Add (MessageHeader header)
73                 {
74                         l.Add (header);
75                 }
76
77                 public void CopyHeaderFrom (Message m, int index)
78                 {
79                         CopyHeaderFrom (m.Headers, index);
80                 }
81
82                 public void Clear ()
83                 {
84                         l.Clear ();
85                 }
86
87                 public void CopyHeaderFrom (MessageHeaders headers, int index)
88                 {
89                         l.Add (headers [index]);
90                 }
91
92                 public void CopyHeadersFrom (Message m)
93                 {
94                         CopyHeadersFrom (m.Headers);
95                 }
96
97                 public void CopyHeadersFrom (MessageHeaders headers)
98                 {
99                         foreach (MessageHeaderInfo h in headers)
100                                 l.Add (h);
101                 }
102
103                 public void CopyTo (MessageHeaderInfo [] dst, int index)
104                 {
105                         l.CopyTo (dst, index);
106                 }
107
108                 public int FindHeader (string name, string ns)
109                 {
110                         return FindHeader (name, ns, null);
111                 }
112
113                 bool HasActor (string actor, string [] candidates)
114                 {
115                         foreach (string c in candidates)
116                                 if (c == actor)
117                                         return true;
118                         return false;
119                 }
120
121                 public int FindHeader (string name, string ns, params string [] actors)
122                 {
123                         int found = 0;
124                         int retval = -1;
125                         
126                         for (int i = 0; i < l.Count; i++) {
127                                 MessageHeaderInfo info = l [i];
128
129                                 if (info.Name == name && info.Namespace == ns) {
130                                         if (found > 0)
131                                                 throw new MessageHeaderException ("Found multiple matching headers.");
132                                         // When no actors are passed, it never
133                                         // matches such header that has an
134                                         // Actor.
135                                         if (actors == null && info.Actor == String.Empty ||
136                                             actors != null && HasActor (info.Actor, actors)) {
137                                                 retval = i;
138                                                 found++;
139                                         }
140                                 }
141                         }
142
143                         return retval;
144                 }
145
146                 public IEnumerator<MessageHeaderInfo> GetEnumerator ()
147                 {
148                         return l.GetEnumerator ();
149                 }
150
151                 XmlObjectSerializer GetSerializer<T> (int headerIndex)
152                 {
153                         if (!serializers.ContainsKey (typeof (T)))
154                                 serializers [typeof (T)] = new DataContractSerializer (typeof (T), this [headerIndex].Name, this [headerIndex].Namespace);
155                         return serializers [typeof (T)];
156                 }
157
158                 public T GetHeader<T> (int index)
159                 {
160                         if (l.Count <= index)
161                                 throw new ArgumentOutOfRangeException ("index");
162                         var dmh = l [index] as MessageHeader.DefaultMessageHeader;
163                         if (dmh != null && dmh.Value != null && typeof (T).IsAssignableFrom (dmh.Value.GetType ()))
164                                 return (T) dmh.Value;
165                         if (typeof (T) == typeof (EndpointAddress)) {
166                                 XmlDictionaryReader r = GetReaderAtHeader (index);
167                                 return r.NodeType != XmlNodeType.Element ? default (T) : (T) (object) EndpointAddress.ReadFrom (r);
168                         }
169                         else
170                                 return GetHeader<T> (index, GetSerializer<T> (index));
171                 }
172
173                 public T GetHeader<T> (int index, XmlObjectSerializer serializer)
174                 {
175                         if (serializer == null)
176                                 throw new ArgumentNullException ("serializer");
177                         XmlDictionaryReader r = GetReaderAtHeader (index);
178                         return (T) serializer.ReadObject (r, false);
179                 }
180
181                 public T GetHeader<T> (string name, string ns)
182                 {
183                         return GetHeader<T> (name, ns, empty_strings);
184                 }
185
186                 public T GetHeader<T> (string name, string ns, params string [] actors)
187                 {
188                         int idx = FindHeader (name, ns, actors);
189
190                         if (idx == -1)
191                                 throw new MessageHeaderException (String.Format ("Header '{0}:{1}' was not found for the argument actors: {2}", ns, name, String.Join (",", actors)));
192
193                         return GetHeader<T> (idx);
194                 }
195
196                 public T GetHeader<T> (string name, string ns, XmlObjectSerializer serializer)
197                 {
198                         if (serializer == null)
199                                 throw new ArgumentNullException ("serializer");
200                         int idx = FindHeader (name, ns);
201
202                         if (idx < 0)
203                                 throw new MessageHeaderException (String.Format ("Header '{0}:{1}' was not found", ns, name));
204
205                         return GetHeader<T> (idx, serializer);
206                 }
207
208                 public XmlDictionaryReader GetReaderAtHeader (int index)
209                 {
210                         if (index >= l.Count)
211                                 throw new ArgumentOutOfRangeException (String.Format ("Index is out of range. Current header count is {0}", index));
212                         MessageHeader item = (MessageHeader) l [index];
213
214                         XmlReader reader =
215                                 item is MessageHeader.RawMessageHeader ?
216                                 ((MessageHeader.RawMessageHeader) item).CreateReader () :
217                                 XmlReader.Create (
218                                         new StringReader (item.ToString ()),
219                                         reader_settings);
220                         reader.MoveToContent ();
221                         XmlDictionaryReader dr = XmlDictionaryReader.CreateDictionaryReader (reader);
222                         dr.MoveToContent ();
223                         return dr;
224                 }
225
226                 public bool HaveMandatoryHeadersBeenUnderstood ()
227                 {
228                         throw new NotImplementedException ();
229                 }
230
231                 public bool HaveMandatoryHeadersBeenUnderstood (params string [] actors)
232                 {
233                         throw new NotImplementedException ();
234                 }
235
236                 public void Insert (int index, MessageHeader header)
237                 {
238                         l.Insert (index, header);
239                 }
240
241                 public void RemoveAll (string name, string ns)
242                 {
243                         // Shuffle all the ones we want to keep to the start of the list
244                         int j = 0;
245                         for (int i = 0; i < l.Count; i++) {
246                                 if (l[i].Name != name || l[i].Namespace != ns) {
247                                         l [j++] = l[i];
248                                 }
249                         }
250                         // Trim the extra elements off the end of the list.
251                         int count = l.Count - j;
252                         for (int i = 0; i < count; i++)
253                                 l.RemoveAt (l.Count - 1);
254                 }
255
256                 public void RemoveAt (int index)
257                 {
258                         l.RemoveAt (index);
259                 }
260
261                 IEnumerator IEnumerable.GetEnumerator ()
262                 {
263                         return ((IEnumerable) l).GetEnumerator ();
264                 }
265
266                 public void WriteHeader (int index, XmlDictionaryWriter writer)
267                 {
268                         if (version.Envelope == EnvelopeVersion.None)
269                                 return;
270
271                         // For AddressingVersion.None, don't output the item.
272                         //
273                         // FIXME: It should even ignore Action, but for now
274                         // service dispatcher won't work without it.
275                         if (version.Addressing == AddressingVersion.None &&
276                             l [index].Name != "Action")
277                                 return;
278
279                         WriteStartHeader (index, writer);
280                         WriteHeaderContents (index, writer);
281                         writer.WriteEndElement ();
282                 }
283
284                 public void WriteHeader (int index, XmlWriter writer)
285                 {
286                         WriteHeader (index, XmlDictionaryWriter.CreateDictionaryWriter (writer));
287                 }
288
289                 public void WriteHeaderContents (int index, XmlDictionaryWriter writer)
290                 {
291                         if (index > l.Count)
292                                 throw new ArgumentOutOfRangeException ("There is no header at position " + index + ".");
293                         
294                         MessageHeader h = l [index] as MessageHeader;
295
296                         h.WriteHeaderContents (writer, version);
297                 }
298
299                 public void WriteHeaderContents (int index, XmlWriter writer)
300                 {
301                         WriteHeaderContents (index, XmlDictionaryWriter.CreateDictionaryWriter (writer));
302                 }
303
304                 public void WriteStartHeader (int index, XmlDictionaryWriter writer)
305                 {
306                         if (index > l.Count)
307                                 throw new ArgumentOutOfRangeException ("There is no header at position " + index + ".");
308
309                         MessageHeader h = l [index] as MessageHeader;
310                         
311                         h.WriteStartHeader (writer, version);
312                 }
313
314                 public void WriteStartHeader (int index, XmlWriter writer)
315                 {
316                         WriteStartHeader (index, XmlDictionaryWriter.CreateDictionaryWriter (writer));
317                 }
318
319                 public string Action {
320                         get {
321                                 int idx = FindHeader ("Action", version.Addressing.Namespace);
322                                 return idx < 0 ? null : GetHeader<string> (idx);
323                         }
324                         set {
325                                 RemoveAll ("Action", version.Addressing.Namespace);
326                                 if (value != null)
327                                         Add (MessageHeader.CreateHeader ("Action", version.Addressing.Namespace, value, true));
328                         }
329                 }
330
331                 public int Count {
332                         get { return l.Count; }
333                 }
334
335                 void AddEndpointAddressHeader (string name, string ns, EndpointAddress address)
336                 {
337                         RemoveAll ("FaultTo", Constants.WsaNamespace);
338                         if (address == null)
339                                 return;
340                         if (MessageVersion.Addressing.Equals (AddressingVersion.WSAddressing10))
341                                 Add (MessageHeader.CreateHeader (name, ns, EndpointAddress10.FromEndpointAddress (address)));
342 #if !NET_2_1
343                         else if (MessageVersion.Addressing.Equals (AddressingVersion.WSAddressingAugust2004))
344                                 Add (MessageHeader.CreateHeader (name, ns, EndpointAddressAugust2004.FromEndpointAddress (address)));
345 #endif
346                         else
347                                 throw new InvalidOperationException ("WS-Addressing header is not allowed for AddressingVersion.None");
348                 }
349
350                 public EndpointAddress FaultTo {
351                         get {
352                                 int idx = FindHeader ("FaultTo", Constants.WsaNamespace);
353                                 return idx < 0 ? null : GetHeader<EndpointAddress> (idx);
354                         }
355                         set { AddEndpointAddressHeader ("FaultTo", Constants.WsaNamespace, value); }
356                 }
357
358                 public EndpointAddress From {
359                         get {
360                                 int idx = FindHeader ("From", version.Addressing.Namespace);
361                                 return idx < 0 ? null : GetHeader<EndpointAddress> (idx);
362                         }
363                         set { AddEndpointAddressHeader ("From", Constants.WsaNamespace, value); }
364                 }
365
366                 public MessageHeaderInfo this [int index] {
367                         get { return l [index]; }
368                 }
369
370                 public UniqueId MessageId {
371                         get { 
372                                 int idx = FindHeader ("MessageID", Constants.WsaNamespace);
373                                 return idx < 0 ? null : new UniqueId (GetHeader<string> (idx));
374                         }
375                         set {
376                                 if (version.Addressing == AddressingVersion.None && value != null)
377                                         throw new InvalidOperationException ("WS-Addressing header is not allowed for AddressingVersion.None");
378
379                                 RemoveAll ("MessageID", Constants.WsaNamespace);
380                                 if (value != null)
381                                         Add (MessageHeader.CreateHeader ("MessageID", Constants.WsaNamespace, value));
382                         }
383                 }
384
385                 public MessageVersion MessageVersion { get { return version; } }
386
387                 public UniqueId RelatesTo {
388                         get { 
389                                 int idx = FindHeader ("RelatesTo", Constants.WsaNamespace);
390                                 return idx < 0 ? null : new UniqueId (GetHeader<string> (idx));
391                         }
392                         set {
393                                 if (version.Addressing == AddressingVersion.None && value != null)
394                                         throw new InvalidOperationException ("WS-Addressing header is not allowed for AddressingVersion.None");
395
396                                 RemoveAll ("MessageID", Constants.WsaNamespace);
397                                 if (value != null)
398                                         Add (MessageHeader.CreateHeader ("RelatesTo", Constants.WsaNamespace, value));
399                         }
400
401                 }
402
403                 public EndpointAddress ReplyTo {
404                         get {
405                                 int idx = FindHeader ("ReplyTo", Constants.WsaNamespace);
406                                 return idx < 0 ? null : GetHeader<EndpointAddress> (idx);
407                         }
408                         set { AddEndpointAddressHeader ("ReplyTo", Constants.WsaNamespace, value); }
409                 }
410
411                 public Uri To {
412                         get {
413                                 int idx = FindHeader ("To", version.Addressing.Namespace);
414                                 //FIXME: return idx < 0 ? null : GetHeader<Uri> (idx);
415                                 return idx < 0 ? null : new Uri (GetHeader<string> (idx));
416                         }
417                         set { 
418                                 RemoveAll ("To", version.Addressing.Namespace);
419                                 if (value != null)
420                                         Add (MessageHeader.CreateHeader ("To", version.Addressing.Namespace, value.AbsoluteUri, true));
421                         }
422                 }
423
424                 [MonoTODO]
425                 public UnderstoodHeaders UnderstoodHeaders {
426                         get { throw new NotImplementedException (); }
427                 }
428
429                 public void SetAction (XmlDictionaryString action)
430                 {
431                         if (action == null)
432                                 Action = null;
433                         else
434                                 Action = action.Value;
435                 }
436         }
437 }