4060586ca1c164ccb7d44b9620101412663ceb7a
[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.XmlMessageHeader ?
214                                 ((MessageHeader.XmlMessageHeader) 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                         if (address == null)
327                                 return;
328                         if (MessageVersion.Addressing.Equals (AddressingVersion.WSAddressing10))
329                                 Add (MessageHeader.CreateHeader (name, ns, EndpointAddress10.FromEndpointAddress (address)));
330 #if !NET_2_1
331                         else if (MessageVersion.Addressing.Equals (AddressingVersion.WSAddressingAugust2004))
332                                 Add (MessageHeader.CreateHeader (name, ns, EndpointAddressAugust2004.FromEndpointAddress (address)));
333 #endif
334                         else
335                                 throw new InvalidOperationException ("WS-Addressing header is not allowed for AddressingVersion.None");
336                 }
337
338                 public EndpointAddress FaultTo {
339                         get {
340                                 int idx = FindHeader ("FaultTo", version.Addressing.Namespace);
341                                 return idx < 0 ? null : GetHeader<EndpointAddress> (idx);
342                         }
343                         set {
344                                 RemoveAll ("FaultTo", version.Addressing.Namespace);
345                                 if (value != null)
346                                         AddEndpointAddressHeader ("FaultTo", version.Addressing.Namespace, value);
347                         }
348                 }
349
350                 public EndpointAddress From {
351                         get {
352                                 int idx = FindHeader ("From", version.Addressing.Namespace);
353                                 return idx < 0 ? null : GetHeader<EndpointAddress> (idx);
354                         }
355                         set {
356                                 RemoveAll ("From", version.Addressing.Namespace);
357                                 if (value != null)
358                                         AddEndpointAddressHeader ("From", version.Addressing.Namespace, value);
359                         }
360                 }
361
362                 public MessageHeaderInfo this [int index] {
363                         get { return l [index]; }
364                 }
365
366                 public UniqueId MessageId {
367                         get { 
368                                 int idx = FindHeader ("MessageID", version.Addressing.Namespace);
369                                 return idx < 0 ? null : new UniqueId (GetHeader<string> (idx));
370                         }
371                         set {
372                                 if (version.Addressing == AddressingVersion.None && value != null)
373                                         throw new InvalidOperationException ("WS-Addressing header is not allowed for AddressingVersion.None");
374
375                                 RemoveAll ("MessageID", version.Addressing.Namespace);
376                                 if (value != null)
377                                         Add (MessageHeader.CreateHeader ("MessageID", version.Addressing.Namespace, value));
378                         }
379                 }
380
381                 public MessageVersion MessageVersion { get { return version; } }
382
383                 public UniqueId RelatesTo {
384                         get { 
385                                 int idx = FindHeader ("RelatesTo", version.Addressing.Namespace);
386                                 return idx < 0 ? null : new UniqueId (GetHeader<string> (idx));
387                         }
388                         set {
389                                 if (version.Addressing == AddressingVersion.None && value != null)
390                                         throw new InvalidOperationException ("WS-Addressing header is not allowed for AddressingVersion.None");
391
392                                 RemoveAll ("MessageID", version.Addressing.Namespace);
393                                 if (value != null)
394                                         Add (MessageHeader.CreateHeader ("RelatesTo", version.Addressing.Namespace, value));
395                         }
396
397                 }
398
399                 public EndpointAddress ReplyTo {
400                         get {
401                                 int idx = FindHeader ("ReplyTo", version.Addressing.Namespace);
402                                 return idx < 0 ? null : GetHeader<EndpointAddress> (idx);
403                         }
404                         set {
405                                 RemoveAll ("ReplyTo", version.Addressing.Namespace);
406                                 if (value != null)
407                                         AddEndpointAddressHeader ("ReplyTo", version.Addressing.Namespace, value);
408                         }
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 }