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 readonly XmlReaderSettings reader_settings
;
42 static MessageHeaders ()
44 reader_settings
= new XmlReaderSettings ();
45 reader_settings
.ConformanceLevel
= ConformanceLevel
.Fragment
;
48 List
<MessageHeaderInfo
> l
;
49 Dictionary
<Type
, XmlObjectSerializer
> serializers
=
50 new Dictionary
<Type
, XmlObjectSerializer
> ();
51 MessageVersion version
;
53 public MessageHeaders (MessageHeaders collection
)
54 : this (collection
.MessageVersion
)
56 CopyHeadersFrom (collection
);
59 public MessageHeaders (MessageVersion version
)
60 : this (version
, 10) // let's say 10 is the initial size
64 public MessageHeaders (MessageVersion version
, int initialSize
)
66 this.version
= version
;
67 l
= new List
<MessageHeaderInfo
> (initialSize
);
70 public void Add (MessageHeader header
)
75 public void CopyHeaderFrom (Message message
, int headerIndex
)
77 CopyHeaderFrom (message
.Headers
, headerIndex
);
85 public void CopyHeaderFrom (MessageHeaders collection
, int headerIndex
)
87 l
.Add (collection
[headerIndex
]);
90 public void CopyHeadersFrom (Message message
)
92 CopyHeadersFrom (message
.Headers
);
95 public void CopyHeadersFrom (MessageHeaders collection
)
97 foreach (MessageHeaderInfo h
in collection
)
101 public void CopyTo (MessageHeaderInfo
[] array
, int index
)
103 l
.CopyTo (array
, index
);
106 public int FindHeader (string name
, string ns
)
108 return FindHeader (name
, ns
, null);
111 bool HasActor (string actor
, string [] candidates
)
113 foreach (string c
in candidates
)
119 public int FindHeader (string name
, string ns
, params string [] actors
)
124 for (int i
= 0; i
< l
.Count
; i
++) {
125 MessageHeaderInfo info
= l
[i
];
127 if (info
.Name
== name
&& info
.Namespace
== ns
) {
129 throw new MessageHeaderException ("Found multiple matching headers.");
130 // When no actors are passed, it never
131 // matches such header that has an
133 if (actors
== null && info
.Actor
== String
.Empty
||
134 actors
!= null && HasActor (info
.Actor
, actors
)) {
144 public IEnumerator
<MessageHeaderInfo
> GetEnumerator ()
146 return l
.GetEnumerator ();
149 XmlObjectSerializer GetSerializer
<T
> (int headerIndex
)
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
)];
156 public T GetHeader
<T
> (int index
)
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
);
168 return GetHeader
<T
> (index
, GetSerializer
<T
> (index
));
171 public T GetHeader
<T
> (int index
, XmlObjectSerializer serializer
)
173 if (serializer
== null)
174 throw new ArgumentNullException ("serializer");
175 XmlDictionaryReader r
= GetReaderAtHeader (index
);
176 return (T
) serializer
.ReadObject (r
, false);
179 public T GetHeader
<T
> (string name
, string ns
)
181 return GetHeader
<T
> (name
, ns
, (string []) null);
184 public T GetHeader
<T
> (string name
, string ns
, params string [] actors
)
186 int idx
= FindHeader (name
, ns
, actors
);
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
)));
191 return GetHeader
<T
> (idx
);
194 public T GetHeader
<T
> (string name
, string ns
, XmlObjectSerializer serializer
)
196 if (serializer
== null)
197 throw new ArgumentNullException ("serializer");
198 int idx
= FindHeader (name
, ns
);
201 throw new MessageHeaderException (String
.Format ("Header '{0}:{1}' was not found", ns
, name
));
203 return GetHeader
<T
> (idx
, serializer
);
206 public XmlDictionaryReader
GetReaderAtHeader (int headerIndex
)
208 if (headerIndex
>= l
.Count
)
209 throw new ArgumentOutOfRangeException (String
.Format ("Index is out of range. Current header count is {0}", l
.Count
));
210 MessageHeader item
= (MessageHeader
) l
[headerIndex
];
213 item
is MessageHeader
.XmlMessageHeader
?
214 ((MessageHeader
.XmlMessageHeader
) item
).CreateReader () :
216 new StringReader (item
.ToString ()),
218 reader
.MoveToContent ();
219 XmlDictionaryReader dr
= XmlDictionaryReader
.CreateDictionaryReader (reader
);
224 public bool HaveMandatoryHeadersBeenUnderstood ()
226 throw new NotImplementedException ();
229 public bool HaveMandatoryHeadersBeenUnderstood (params string [] actors
)
231 throw new NotImplementedException ();
234 public void Insert (int headerIndex
, MessageHeader header
)
236 l
.Insert (headerIndex
, header
);
239 public void RemoveAll (string name
, string ns
)
241 // Shuffle all the ones we want to keep to the start of the list
243 for (int i
= 0; i
< l
.Count
; i
++) {
244 if (l
[i
].Name
!= name
|| l
[i
].Namespace
!= ns
) {
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);
254 public void RemoveAt (int headerIndex
)
256 l
.RemoveAt (headerIndex
);
259 IEnumerator IEnumerable
.GetEnumerator ()
261 return ((IEnumerable
) l
).GetEnumerator ();
264 public void WriteHeader (int headerIndex
, XmlDictionaryWriter writer
)
266 if (version
.Envelope
== EnvelopeVersion
.None
)
268 WriteStartHeader (headerIndex
, writer
);
269 WriteHeaderContents (headerIndex
, writer
);
270 writer
.WriteEndElement ();
273 public void WriteHeader (int headerIndex
, XmlWriter writer
)
275 WriteHeader (headerIndex
, XmlDictionaryWriter
.CreateDictionaryWriter (writer
));
278 public void WriteHeaderContents (int headerIndex
, XmlDictionaryWriter writer
)
280 if (headerIndex
> l
.Count
)
281 throw new ArgumentOutOfRangeException ("There is no header at position " + headerIndex
+ ".");
283 MessageHeader h
= l
[headerIndex
] as MessageHeader
;
285 h
.WriteHeaderContents (writer
, version
);
288 public void WriteHeaderContents (int headerIndex
, XmlWriter writer
)
290 WriteHeaderContents (headerIndex
, XmlDictionaryWriter
.CreateDictionaryWriter (writer
));
293 public void WriteStartHeader (int headerIndex
, XmlDictionaryWriter writer
)
295 if (headerIndex
> l
.Count
)
296 throw new ArgumentOutOfRangeException ("There is no header at position " + headerIndex
+ ".");
298 MessageHeader h
= l
[headerIndex
] as MessageHeader
;
300 h
.WriteStartHeader (writer
, version
);
303 public void WriteStartHeader (int headerIndex
, XmlWriter writer
)
305 WriteStartHeader (headerIndex
, XmlDictionaryWriter
.CreateDictionaryWriter (writer
));
308 public string Action
{
310 int idx
= FindHeader ("Action", version
.Addressing
.Namespace
);
311 return idx
< 0 ? null : GetHeader
<string> (idx
);
314 RemoveAll ("Action", version
.Addressing
.Namespace
);
316 Add (MessageHeader
.CreateHeader ("Action", version
.Addressing
.Namespace
, value, true));
321 get { return l.Count; }
324 void AddEndpointAddressHeader (string name
, string ns
, EndpointAddress address
)
328 if (MessageVersion
.Addressing
.Equals (AddressingVersion
.WSAddressing10
))
329 Add (MessageHeader
.CreateHeader (name
, ns
, EndpointAddress10
.FromEndpointAddress (address
)));
331 else if (MessageVersion
.Addressing
.Equals (AddressingVersion
.WSAddressingAugust2004
))
332 Add (MessageHeader
.CreateHeader (name
, ns
, EndpointAddressAugust2004
.FromEndpointAddress (address
)));
335 throw new InvalidOperationException ("WS-Addressing header is not allowed for AddressingVersion.None");
338 public EndpointAddress FaultTo
{
340 int idx
= FindHeader ("FaultTo", version
.Addressing
.Namespace
);
341 return idx
< 0 ? null : GetHeader
<EndpointAddress
> (idx
);
344 RemoveAll ("FaultTo", version
.Addressing
.Namespace
);
346 AddEndpointAddressHeader ("FaultTo", version
.Addressing
.Namespace
, value);
350 public EndpointAddress From
{
352 int idx
= FindHeader ("From", version
.Addressing
.Namespace
);
353 return idx
< 0 ? null : GetHeader
<EndpointAddress
> (idx
);
356 RemoveAll ("From", version
.Addressing
.Namespace
);
358 AddEndpointAddressHeader ("From", version
.Addressing
.Namespace
, value);
362 public MessageHeaderInfo
this [int index
] {
363 get { return l [index]; }
366 public UniqueId MessageId
{
368 int idx
= FindHeader ("MessageID", version
.Addressing
.Namespace
);
369 return idx
< 0 ? null : new UniqueId (GetHeader
<string> (idx
));
372 if (version
.Addressing
== AddressingVersion
.None
&& value != null)
373 throw new InvalidOperationException ("WS-Addressing header is not allowed for AddressingVersion.None");
375 RemoveAll ("MessageID", version
.Addressing
.Namespace
);
377 Add (MessageHeader
.CreateHeader ("MessageID", version
.Addressing
.Namespace
, value));
381 public MessageVersion MessageVersion { get { return version; }
}
383 public UniqueId RelatesTo
{
385 int idx
= FindHeader ("RelatesTo", version
.Addressing
.Namespace
);
386 return idx
< 0 ? null : new UniqueId (GetHeader
<string> (idx
));
389 if (version
.Addressing
== AddressingVersion
.None
&& value != null)
390 throw new InvalidOperationException ("WS-Addressing header is not allowed for AddressingVersion.None");
392 RemoveAll ("MessageID", version
.Addressing
.Namespace
);
394 Add (MessageHeader
.CreateHeader ("RelatesTo", version
.Addressing
.Namespace
, value));
399 public EndpointAddress ReplyTo
{
401 int idx
= FindHeader ("ReplyTo", version
.Addressing
.Namespace
);
402 return idx
< 0 ? null : GetHeader
<EndpointAddress
> (idx
);
405 RemoveAll ("ReplyTo", version
.Addressing
.Namespace
);
407 AddEndpointAddressHeader ("ReplyTo", version
.Addressing
.Namespace
, value);
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
));
418 RemoveAll ("To", version
.Addressing
.Namespace
);
420 Add (MessageHeader
.CreateHeader ("To", version
.Addressing
.Namespace
, value.AbsoluteUri
, true));
425 public UnderstoodHeaders UnderstoodHeaders
{
426 get { throw new NotImplementedException (); }
429 public void SetAction (XmlDictionaryString action
)
434 Action
= action
.Value
;