2 // System.ServiceModel.MessageHeader.cs
4 // Author: Duncan Mak (duncan@novell.com)
6 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
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:
16 // The above copyright notice and this permission notice shall be
17 // included in all copies or substantial portions of the Software.
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.
29 using System
.Collections
;
30 using System
.Collections
.Generic
;
32 using System
.Runtime
.Serialization
;
33 using System
.ServiceModel
;
36 namespace System
.ServiceModel
.Channels
38 public sealed class MessageHeaders
: IEnumerable
<MessageHeaderInfo
>, IEnumerable
40 static string [] empty_strings
= new string [0];
42 static readonly XmlReaderSettings reader_settings
;
44 static MessageHeaders ()
46 reader_settings
= new XmlReaderSettings ();
47 reader_settings
.ConformanceLevel
= ConformanceLevel
.Fragment
;
50 List
<MessageHeaderInfo
> l
;
51 Dictionary
<Type
, XmlObjectSerializer
> serializers
=
52 new Dictionary
<Type
, XmlObjectSerializer
> ();
53 MessageVersion version
;
55 public MessageHeaders (MessageHeaders headers
)
56 : this (headers
.MessageVersion
)
58 CopyHeadersFrom (headers
);
61 public MessageHeaders (MessageVersion version
)
62 : this (version
, 10) // let's say 10 is the initial size
66 public MessageHeaders (MessageVersion version
, int capacity
)
68 this.version
= version
;
69 l
= new List
<MessageHeaderInfo
> (capacity
);
72 public void Add (MessageHeader header
)
77 public void CopyHeaderFrom (Message m
, int index
)
79 CopyHeaderFrom (m
.Headers
, index
);
87 public void CopyHeaderFrom (MessageHeaders headers
, int index
)
89 l
.Add (headers
[index
]);
92 public void CopyHeadersFrom (Message m
)
94 CopyHeadersFrom (m
.Headers
);
97 public void CopyHeadersFrom (MessageHeaders headers
)
99 foreach (MessageHeaderInfo h
in headers
)
103 public void CopyTo (MessageHeaderInfo
[] dst
, int index
)
105 l
.CopyTo (dst
, index
);
108 public int FindHeader (string name
, string ns
)
110 return FindHeader (name
, ns
, null);
113 bool HasActor (string actor
, string [] candidates
)
115 foreach (string c
in candidates
)
121 public int FindHeader (string name
, string ns
, params string [] actors
)
126 for (int i
= 0; i
< l
.Count
; i
++) {
127 MessageHeaderInfo info
= l
[i
];
129 if (info
.Name
== name
&& info
.Namespace
== ns
) {
131 throw new ArgumentException ("Found multiple matching headers.");
132 // When no actors are passed, it never
133 // matches such header that has an
135 if (actors
== null && info
.Actor
== String
.Empty
||
136 actors
!= null && HasActor (info
.Actor
, actors
)) {
146 public IEnumerator
<MessageHeaderInfo
> GetEnumerator ()
148 return l
.GetEnumerator ();
151 XmlObjectSerializer GetSerializer
<T
> (int headerIndex
)
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
)];
158 public T GetHeader
<T
> (int index
)
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
);
170 return GetHeader
<T
> (index
, GetSerializer
<T
> (index
));
173 public T GetHeader
<T
> (int index
, XmlObjectSerializer serializer
)
175 if (serializer
== null)
176 throw new ArgumentNullException ("serializer");
177 XmlDictionaryReader r
= GetReaderAtHeader (index
);
178 return (T
) serializer
.ReadObject (r
, false);
181 public T GetHeader
<T
> (string name
, string ns
)
183 return GetHeader
<T
> (name
, ns
, empty_strings
);
186 public T GetHeader
<T
> (string name
, string ns
, params string [] actors
)
188 int idx
= FindHeader (name
, ns
, actors
);
191 throw new MessageHeaderException (String
.Format ("Header '{0}:{1}' was not found for the argument actors: {2}", ns
, name
, String
.Join (",", actors
)));
193 return GetHeader
<T
> (idx
);
196 public T GetHeader
<T
> (string name
, string ns
, XmlObjectSerializer serializer
)
198 if (serializer
== null)
199 throw new ArgumentNullException ("serializer");
200 int idx
= FindHeader (name
, ns
);
203 throw new MessageHeaderException (String
.Format ("Header '{0}:{1}' was not found", ns
, name
));
205 return GetHeader
<T
> (idx
, serializer
);
208 public XmlDictionaryReader
GetReaderAtHeader (int index
)
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
];
215 item
is MessageHeader
.RawMessageHeader
?
216 ((MessageHeader
.RawMessageHeader
) item
).CreateReader () :
218 new StringReader (item
.ToString ()),
220 reader
.MoveToContent ();
221 XmlDictionaryReader dr
= XmlDictionaryReader
.CreateDictionaryReader (reader
);
226 public bool HaveMandatoryHeadersBeenUnderstood ()
228 throw new NotImplementedException ();
231 public bool HaveMandatoryHeadersBeenUnderstood (params string [] actors
)
233 throw new NotImplementedException ();
236 public void Insert (int index
, MessageHeader header
)
238 l
.Insert (index
, header
);
241 public void RemoveAll (string name
, string ns
)
243 // Shuffle all the ones we want to keep to the start of the list
245 for (int i
= 0; i
< l
.Count
; i
++) {
246 if (l
[i
].Name
!= name
|| l
[i
].Namespace
!= ns
) {
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);
256 public void RemoveAt (int index
)
261 IEnumerator IEnumerable
.GetEnumerator ()
263 return ((IEnumerable
) l
).GetEnumerator ();
266 public void WriteHeader (int index
, XmlDictionaryWriter writer
)
268 if (version
.Envelope
== EnvelopeVersion
.None
)
271 // For AddressingVersion.None, don't output the item.
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")
279 WriteStartHeader (index
, writer
);
280 WriteHeaderContents (index
, writer
);
281 writer
.WriteEndElement ();
284 public void WriteHeader (int index
, XmlWriter writer
)
286 WriteHeader (index
, XmlDictionaryWriter
.CreateDictionaryWriter (writer
));
289 public void WriteHeaderContents (int index
, XmlDictionaryWriter writer
)
292 throw new ArgumentOutOfRangeException ("There is no header at position " + index
+ ".");
294 MessageHeader h
= l
[index
] as MessageHeader
;
296 h
.WriteHeaderContents (writer
, version
);
299 public void WriteHeaderContents (int index
, XmlWriter writer
)
301 WriteHeaderContents (index
, XmlDictionaryWriter
.CreateDictionaryWriter (writer
));
304 public void WriteStartHeader (int index
, XmlDictionaryWriter writer
)
307 throw new ArgumentOutOfRangeException ("There is no header at position " + index
+ ".");
309 MessageHeader h
= l
[index
] as MessageHeader
;
311 h
.WriteStartHeader (writer
, version
);
314 public void WriteStartHeader (int index
, XmlWriter writer
)
316 WriteStartHeader (index
, XmlDictionaryWriter
.CreateDictionaryWriter (writer
));
319 public string Action
{
321 int idx
= FindHeader ("Action", version
.Addressing
.Namespace
);
322 return idx
< 0 ? null : GetHeader
<string> (idx
);
325 RemoveAll ("Action", version
.Addressing
.Namespace
);
327 Add (MessageHeader
.CreateHeader ("Action", version
.Addressing
.Namespace
, value, true));
332 get { return l.Count; }
336 public EndpointAddress FaultTo
{
338 int idx
= FindHeader ("FaultTo", Constants
.WSA1
);
339 return idx
< 0 ? null : GetHeader
<EndpointAddress
> (idx
);
342 if (version
.Addressing
== AddressingVersion
.None
)
343 throw new InvalidOperationException ("WS-Addressing header is not allowed for AddressingVersion.None");
345 RemoveAll ("FaultTo", Constants
.WSA1
);
347 Add (MessageHeader
.CreateHeader ("FaultTo", Constants
.WSA1
, EndpointAddress10
.FromEndpointAddress (value)));
351 public EndpointAddress From
{
353 int idx
= FindHeader ("From", version
.Addressing
.Namespace
);
354 return idx
< 0 ? null : GetHeader
<EndpointAddress
> (idx
);
357 if (version
.Addressing
== AddressingVersion
.None
)
358 throw new InvalidOperationException ("WS-Addressing header is not allowed for AddressingVersion.None");
360 RemoveAll ("From", Constants
.WSA1
);
362 Add (MessageHeader
.CreateHeader ("From", Constants
.WSA1
, EndpointAddress10
.FromEndpointAddress (value)));
367 public MessageHeaderInfo
this [int index
] {
368 get { return l [index]; }
371 public UniqueId MessageId
{
373 int idx
= FindHeader ("MessageID", Constants
.WSA1
);
374 return idx
< 0 ? null : new UniqueId (GetHeader
<string> (idx
));
377 if (version
.Addressing
== AddressingVersion
.None
)
378 throw new InvalidOperationException ("WS-Addressing header is not allowed for AddressingVersion.None");
380 RemoveAll ("MessageID", Constants
.WSA1
);
382 Add (MessageHeader
.CreateHeader ("MessageID", Constants
.WSA1
, value.ToString ()));
386 public MessageVersion MessageVersion { get { return version; }
}
388 public UniqueId RelatesTo
{
390 int idx
= FindHeader ("RelatesTo", Constants
.WSA1
);
391 return idx
< 0 ? null : new UniqueId (GetHeader
<string> (idx
));
394 if (version
.Addressing
== AddressingVersion
.None
)
395 throw new InvalidOperationException ("WS-Addressing header is not allowed for AddressingVersion.None");
397 RemoveAll ("MessageID", Constants
.WSA1
);
399 Add (MessageHeader
.CreateHeader ("RelatesTo", Constants
.WSA1
, value.ToString ()));
405 public EndpointAddress ReplyTo
{
407 int idx
= FindHeader ("ReplyTo", Constants
.WSA1
);
408 return idx
< 0 ? null : GetHeader
<EndpointAddress
> (idx
);
411 if (version
.Addressing
== AddressingVersion
.None
)
412 throw new InvalidOperationException ("WS-Addressing header is not allowed for AddressingVersion.None");
414 RemoveAll ("ReplyTo", Constants
.WSA1
);
416 Add (MessageHeader
.CreateHeader ("ReplyTo", Constants
.WSA1
, EndpointAddress10
.FromEndpointAddress (value)));
423 int idx
= FindHeader ("To", version
.Addressing
.Namespace
);
424 //FIXME: return idx < 0 ? null : GetHeader<Uri> (idx);
425 return idx
< 0 ? null : new Uri (GetHeader
<string> (idx
));
428 RemoveAll ("To", version
.Addressing
.Namespace
);
430 Add (MessageHeader
.CreateHeader ("To", version
.Addressing
.Namespace
, value.AbsoluteUri
, true));
435 public UnderstoodHeaders UnderstoodHeaders
{
436 get { throw new NotImplementedException (); }