MessageHeaders.cs 12 KB

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