// // System.ServiceModel.MessageHeader.cs // // Author: Duncan Mak (duncan@novell.com) // Atsushi Enomoto (atsushi@ximian.com) // // Copyright (C) 2005 Novell, Inc (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.IO; using System.Runtime.Serialization; using System.ServiceModel; using System.ServiceModel.Channels; using System.Text; using System.Xml; namespace System.ServiceModel.Channels { public abstract class MessageHeader : MessageHeaderInfo { static readonly XmlWriterSettings writer_settings; static MessageHeader () { writer_settings = new XmlWriterSettings (); writer_settings.OmitXmlDeclaration = true; writer_settings.Indent = true; } protected MessageHeader () {} static string default_actor = String.Empty; static bool default_is_ref = false; static bool default_must_understand = false; static bool default_relay = false; public static MessageHeader CreateHeader (string name, string ns, object value) { return CreateHeader (name, ns, value, default_must_understand); } public static MessageHeader CreateHeader (string name, string ns, object value, bool must_understand) { return CreateHeader (name, ns, value, must_understand, default_actor); } public static MessageHeader CreateHeader (string name, string ns, object value, XmlObjectSerializer formatter) { return CreateHeader (name, ns, value, formatter, default_must_understand, default_actor, default_relay); } public static MessageHeader CreateHeader (string name, string ns, object value, bool must_understand, string actor) { return CreateHeader (name, ns, value, must_understand, actor, default_relay); } public static MessageHeader CreateHeader (string name, string ns, object value, XmlObjectSerializer formatter, bool must_understand) { return CreateHeader (name, ns, value, formatter, must_understand, default_actor, default_relay); } public static MessageHeader CreateHeader (string name, string ns, object value, bool must_understand, string actor, bool relay) { return CreateHeader (name, ns, value, new DataContractSerializer (value.GetType ()), must_understand, actor, relay); } public static MessageHeader CreateHeader (string name, string ns, object value, XmlObjectSerializer formatter, bool must_understand, string actor) { return CreateHeader (name, ns, value, formatter, must_understand, actor, default_relay); } public static MessageHeader CreateHeader (string name, string ns, object value, XmlObjectSerializer formatter, bool must_understand, string actor, bool relay) { // FIXME: how to get IsReferenceParameter ? return new DefaultMessageHeader (name, ns, value, formatter, default_is_ref, must_understand, actor, relay); } public virtual bool IsMessageVersionSupported (MessageVersion version) { if (version.Envelope == EnvelopeVersion.Soap12) if (Actor == EnvelopeVersion.Soap11.NextDestinationActorValue) return false; if (version.Envelope == EnvelopeVersion.Soap11) if (Actor == EnvelopeVersion.Soap12.NextDestinationActorValue || Actor == EnvelopeVersion.Soap12UltimateReceiver) return false; // by default, it's always supported return true; } protected abstract void OnWriteHeaderContents (XmlDictionaryWriter writer, MessageVersion version); protected virtual void OnWriteStartHeader (XmlDictionaryWriter writer, MessageVersion version) { var dic = Constants.SoapDictionary; XmlDictionaryString name, ns; var prefix = Namespace.Length > 0 ? writer.LookupPrefix (Namespace) : String.Empty; if (dic.TryLookup (Name, out name) && dic.TryLookup (Namespace, out ns)) writer.WriteStartElement (prefix, name, ns); else writer.WriteStartElement (prefix, this.Name, this.Namespace); WriteHeaderAttributes (writer, version); } public override string ToString () { StringBuilder sb = new StringBuilder (); XmlWriter w = XmlWriter.Create (sb, writer_settings); WriteHeader (w, MessageVersion.Default); w.Close (); return sb.ToString (); } public void WriteHeader (XmlDictionaryWriter writer, MessageVersion version) { if (writer == null) throw new ArgumentNullException ("writer is null."); if (version == null) throw new ArgumentNullException ("version is null."); if (version.Envelope == EnvelopeVersion.None) return; WriteStartHeader (writer, version); WriteHeaderContents (writer, version); writer.WriteEndElement (); } public void WriteHeader (XmlWriter writer, MessageVersion version) { WriteHeader (XmlDictionaryWriter.CreateDictionaryWriter (writer), version); } protected void WriteHeaderAttributes (XmlDictionaryWriter writer, MessageVersion version) { var dic = Constants.SoapDictionary; if (Id != null) writer.WriteAttributeString ("u", dic.Add ("Id"), dic.Add (Constants.WsuNamespace), Id); if (Actor != String.Empty) { if (version.Envelope == EnvelopeVersion.Soap11) writer.WriteAttributeString ("s", dic.Add ("actor"), dic.Add (version.Envelope.Namespace), Actor); if (version.Envelope == EnvelopeVersion.Soap12) writer.WriteAttributeString ("s", dic.Add ("role"), dic.Add (version.Envelope.Namespace), Actor); } // mustUnderstand is the same across SOAP 1.1 and 1.2 if (MustUnderstand == true) writer.WriteAttributeString ("s", dic.Add ("mustUnderstand"), dic.Add (version.Envelope.Namespace), "1"); // relay is only available on SOAP 1.2 if (Relay == true && version.Envelope == EnvelopeVersion.Soap12) writer.WriteAttributeString ("s", dic.Add ("relay"), dic.Add (version.Envelope.Namespace), "true"); } public void WriteHeaderContents (XmlDictionaryWriter writer, MessageVersion version) { this.OnWriteHeaderContents (writer, version); } public void WriteStartHeader (XmlDictionaryWriter writer, MessageVersion version) { this.OnWriteStartHeader (writer, version); } public override string Actor { get { return default_actor; }} public override bool IsReferenceParameter { get { return default_is_ref; }} public override bool MustUnderstand { get { return default_must_understand; }} public override bool Relay { get { return default_relay; }} internal class RawMessageHeader : MessageHeader { string soap_ns; bool is_ref, must_understand, relay; string actor; string body; string local_name; string namespace_uri; public RawMessageHeader (XmlReader reader, string soap_ns) { Id = reader.GetAttribute ("Id", Constants.WsuNamespace); string s = reader.GetAttribute ("relay", soap_ns); relay = s != null ? XmlConvert.ToBoolean (s) : false; s = reader.GetAttribute ("mustUnderstand", soap_ns); must_understand = s != null ? XmlConvert.ToBoolean (s) : false; actor = reader.GetAttribute ("actor", soap_ns) ?? String.Empty; s = reader.GetAttribute ("IsReferenceParameter", Constants.WsaNamespace); is_ref = s != null ? XmlConvert.ToBoolean (s) : false; local_name = reader.LocalName; namespace_uri = reader.NamespaceURI; body = reader.ReadOuterXml (); } public XmlReader CreateReader () { var reader = XmlReader.Create (new StringReader (body)); reader.MoveToContent (); return reader; } protected override void OnWriteHeaderContents ( XmlDictionaryWriter writer, MessageVersion version) { var r = CreateReader (); r.MoveToContent (); if (r.IsEmptyElement) return; // write nothing for (r.Read (); r.NodeType != XmlNodeType.EndElement;) writer.WriteNode (r, false); } public override string Actor { get { return actor; }} public override bool IsReferenceParameter { get { return is_ref; }} public override bool MustUnderstand { get { return must_understand; }} public override string Name { get { return local_name; }} public override string Namespace { get { return namespace_uri; }} public override bool Relay { get { return relay; }} } internal class DefaultMessageHeader : MessageHeader { string actor, name, ns; object value; XmlObjectSerializer formatter; bool is_ref, must_understand, relay; internal DefaultMessageHeader (string name, string ns, object value, XmlObjectSerializer formatter, bool isReferenceParameter, bool mustUnderstand, string actor, bool relay) { this.name = name; this.ns = ns; this.value = value; this.formatter = formatter; this.is_ref = isReferenceParameter; this.must_understand = mustUnderstand; this.actor = actor; this.relay = relay; } protected override void OnWriteHeaderContents (XmlDictionaryWriter writer, MessageVersion version) { // FIXME: it's a nasty workaround just to avoid UniqueId output as a string, for bug #577139. if (Value is UniqueId) writer.WriteValue ((UniqueId) Value); else this.formatter.WriteObjectContent (writer, value); } public object Value { get { return value; } } public override string Actor { get { return actor; }} public override bool IsReferenceParameter { get { return is_ref; }} public override bool MustUnderstand { get { return must_understand; }} public override string Name { get { return name; }} public override string Namespace { get { return ns; }} public override bool Relay { get { return relay; }} } } }