File: System\ServiceModel\Channels\MessageHeader.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
namespace System.ServiceModel.Channels
{
    using System.Globalization;
    using System.IO;
    using System.Runtime;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.ServiceModel.Description;
    using System.ServiceModel.Dispatcher;
    using System.Xml;
 
    public abstract class MessageHeader : MessageHeaderInfo
    {
        const bool DefaultRelayValue = false;
        const bool DefaultMustUnderstandValue = false;
        const string DefaultActorValue = "";
 
        public override string Actor
        {
            get { return ""; }
        }
 
        public override bool IsReferenceParameter
        {
            get { return false; }
        }
 
        public override bool MustUnderstand
        {
            get { return DefaultMustUnderstandValue; }
        }
 
        public override bool Relay
        {
            get { return DefaultRelayValue; }
        }
 
        public virtual bool IsMessageVersionSupported(MessageVersion messageVersion)
        {
            if (messageVersion == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageVersion");
            }
 
            return true;
        }
 
        public override string ToString()
        {
            StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture);
            XmlTextWriter textWriter = new XmlTextWriter(stringWriter);
            textWriter.Formatting = Formatting.Indented;
            XmlDictionaryWriter writer = XmlDictionaryWriter.CreateDictionaryWriter(textWriter);
 
            if (IsMessageVersionSupported(MessageVersion.Soap12WSAddressing10))
            {
                WriteHeader(writer, MessageVersion.Soap12WSAddressing10);
            }
            else if (IsMessageVersionSupported(MessageVersion.Soap12WSAddressingAugust2004))
            {
                WriteHeader(writer, MessageVersion.Soap12WSAddressingAugust2004);
            }
            else if (IsMessageVersionSupported(MessageVersion.Soap11WSAddressing10))
            {
                WriteHeader(writer, MessageVersion.Soap11WSAddressing10);
            }
            else if (IsMessageVersionSupported(MessageVersion.Soap11WSAddressingAugust2004))
            {
                WriteHeader(writer, MessageVersion.Soap11WSAddressingAugust2004);
            }
            else if (IsMessageVersionSupported(MessageVersion.Soap12))
            {
                WriteHeader(writer, MessageVersion.Soap12);
            }
            else if (IsMessageVersionSupported(MessageVersion.Soap11))
            {
                WriteHeader(writer, MessageVersion.Soap11);
            }
            else
            {
                WriteHeader(writer, MessageVersion.None);
            }
 
            writer.Flush();
            return stringWriter.ToString();
        }
 
        public void WriteHeader(XmlWriter writer, MessageVersion messageVersion)
        {
            WriteHeader(XmlDictionaryWriter.CreateDictionaryWriter(writer), messageVersion);
        }
 
        public void WriteHeader(XmlDictionaryWriter writer, MessageVersion messageVersion)
        {
            if (writer == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("writer"));
            if (messageVersion == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("messageVersion"));
            OnWriteStartHeader(writer, messageVersion);
            OnWriteHeaderContents(writer, messageVersion);
            writer.WriteEndElement();
        }
 
        public void WriteStartHeader(XmlDictionaryWriter writer, MessageVersion messageVersion)
        {
            if (writer == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("writer"));
            if (messageVersion == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("messageVersion"));
            OnWriteStartHeader(writer, messageVersion);
        }
 
        protected virtual void OnWriteStartHeader(XmlDictionaryWriter writer, MessageVersion messageVersion)
        {
            writer.WriteStartElement(this.Name, this.Namespace);
            WriteHeaderAttributes(writer, messageVersion);
        }
 
        public void WriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
        {
            if (writer == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("writer"));
            if (messageVersion == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("messageVersion"));
            OnWriteHeaderContents(writer, messageVersion);
        }
 
        protected abstract void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion);
 
        protected void WriteHeaderAttributes(XmlDictionaryWriter writer, MessageVersion messageVersion)
        {
            string actor = this.Actor;
            if (actor.Length > 0)
                writer.WriteAttributeString(messageVersion.Envelope.DictionaryActor, messageVersion.Envelope.DictionaryNamespace, actor);
            if (this.MustUnderstand)
                writer.WriteAttributeString(XD.MessageDictionary.MustUnderstand, messageVersion.Envelope.DictionaryNamespace, "1");
            if (this.Relay && messageVersion.Envelope == EnvelopeVersion.Soap12)
                writer.WriteAttributeString(XD.Message12Dictionary.Relay, XD.Message12Dictionary.Namespace, "1");
        }
 
        public static MessageHeader CreateHeader(string name, string ns, object value)
        {
            return CreateHeader(name, ns, value, DefaultMustUnderstandValue, DefaultActorValue, DefaultRelayValue);
        }
 
        public static MessageHeader CreateHeader(string name, string ns, object value, bool mustUnderstand)
        {
            return CreateHeader(name, ns, value, mustUnderstand, DefaultActorValue, DefaultRelayValue);
        }
 
        public static MessageHeader CreateHeader(string name, string ns, object value, bool mustUnderstand, string actor)
        {
            return CreateHeader(name, ns, value, mustUnderstand, actor, DefaultRelayValue);
        }
 
        public static MessageHeader CreateHeader(string name, string ns, object value, bool mustUnderstand, string actor, bool relay)
        {
            return new XmlObjectSerializerHeader(name, ns, value, null, mustUnderstand, actor, relay);
        }
 
        public static MessageHeader CreateHeader(string name, string ns, object value, XmlObjectSerializer serializer)
        {
            return CreateHeader(name, ns, value, serializer, DefaultMustUnderstandValue, DefaultActorValue, DefaultRelayValue);
        }
 
        public static MessageHeader CreateHeader(string name, string ns, object value, XmlObjectSerializer serializer, bool mustUnderstand)
        {
            return CreateHeader(name, ns, value, serializer, mustUnderstand, DefaultActorValue, DefaultRelayValue);
        }
 
        public static MessageHeader CreateHeader(string name, string ns, object value, XmlObjectSerializer serializer, bool mustUnderstand, string actor)
        {
            return CreateHeader(name, ns, value, serializer, mustUnderstand, actor, DefaultRelayValue);
        }
 
        public static MessageHeader CreateHeader(string name, string ns, object value, XmlObjectSerializer serializer, bool mustUnderstand, string actor, bool relay)
        {
            if (serializer == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("serializer"));
 
            return new XmlObjectSerializerHeader(name, ns, value, serializer, mustUnderstand, actor, relay);
        }
 
        internal static void GetHeaderAttributes(XmlDictionaryReader reader, MessageVersion version,
            out string actor, out bool mustUnderstand, out bool relay, out bool isReferenceParameter)
        {
            int attributeCount = reader.AttributeCount;
 
            if (attributeCount == 0)
            {
                mustUnderstand = false;
                actor = string.Empty;
                relay = false;
                isReferenceParameter = false;
            }
            else
            {
                string mustUnderstandString = reader.GetAttribute(XD.MessageDictionary.MustUnderstand, version.Envelope.DictionaryNamespace);
                if (mustUnderstandString != null && ToBoolean(mustUnderstandString))
                    mustUnderstand = true;
                else
                    mustUnderstand = false;
 
                if (mustUnderstand && attributeCount == 1)
                {
                    actor = string.Empty;
                    relay = false;
                }
                else
                {
                    actor = reader.GetAttribute(version.Envelope.DictionaryActor, version.Envelope.DictionaryNamespace);
                    if (actor == null)
                        actor = "";
 
                    if (version.Envelope == EnvelopeVersion.Soap12)
                    {
                        string relayString = reader.GetAttribute(XD.Message12Dictionary.Relay, version.Envelope.DictionaryNamespace);
                        if (relayString != null && ToBoolean(relayString))
                            relay = true;
                        else
                            relay = false;
                    }
                    else
                    {
                        relay = false;
                    }
                }
 
                isReferenceParameter = false;
                if (version.Addressing == AddressingVersion.WSAddressing10)
                {
                    string refParam = reader.GetAttribute(XD.AddressingDictionary.IsReferenceParameter, version.Addressing.DictionaryNamespace);
                    if (refParam != null)
                        isReferenceParameter = ToBoolean(refParam);
                }
            }
        }
 
        static bool ToBoolean(string value)
        {
            if (value.Length == 1)
            {
                char ch = value[0];
                if (ch == '1')
                {
                    return true;
                }
                if (ch == '0')
                {
                    return false;
                }
            }
            else
            {
                if (value == "true")
                {
                    return true;
                }
                else if (value == "false")
                {
                    return false;
                }
            }
            try
            {
                return XmlConvert.ToBoolean(value);
            }
            catch (FormatException exception)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(exception.Message, null));
            }
        }
    }
 
    abstract class DictionaryHeader : MessageHeader
    {
        public override string Name
        {
            get { return DictionaryName.Value; }
        }
 
        public override string Namespace
        {
            get { return DictionaryNamespace.Value; }
        }
 
        public abstract XmlDictionaryString DictionaryName { get; }
        public abstract XmlDictionaryString DictionaryNamespace { get; }
 
        protected override void OnWriteStartHeader(XmlDictionaryWriter writer, MessageVersion messageVersion)
        {
            writer.WriteStartElement(DictionaryName, DictionaryNamespace);
            WriteHeaderAttributes(writer, messageVersion);
        }
    }
 
    class XmlObjectSerializerHeader : MessageHeader
    {
        XmlObjectSerializer serializer;
        bool mustUnderstand;
        bool relay;
        bool isOneTwoSupported;
        bool isOneOneSupported;
        bool isNoneSupported;
        object objectToSerialize;
        string name;
        string ns;
        string actor;
        object syncRoot = new object();
 
        XmlObjectSerializerHeader(XmlObjectSerializer serializer, bool mustUnderstand, string actor, bool relay)
        {
            if (actor == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("actor");
            }
 
            this.mustUnderstand = mustUnderstand;
            this.relay = relay;
            this.serializer = serializer;
            this.actor = actor;
            if (actor == EnvelopeVersion.Soap12.UltimateDestinationActor)
            {
                this.isOneOneSupported = false;
                this.isOneTwoSupported = true;
            }
            else if (actor == EnvelopeVersion.Soap12.NextDestinationActorValue)
            {
                this.isOneOneSupported = false;
                this.isOneTwoSupported = true;
            }
            else if (actor == EnvelopeVersion.Soap11.NextDestinationActorValue)
            {
                this.isOneOneSupported = true;
                this.isOneTwoSupported = false;
            }
            else
            {
                this.isOneOneSupported = true;
                this.isOneTwoSupported = true;
                this.isNoneSupported = true;
            }
        }
 
        public XmlObjectSerializerHeader(string name, string ns, object objectToSerialize, XmlObjectSerializer serializer, bool mustUnderstand, string actor, bool relay)
            : this(serializer, mustUnderstand, actor, relay)
        {
            if (null == name)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("name"));
            }
 
            if (name.Length == 0)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.SFXHeaderNameCannotBeNullOrEmpty), "name"));
            }
 
            if (ns == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("ns");
            }
            if (ns.Length > 0)
            {
                NamingHelper.CheckUriParameter(ns, "ns");
            }
            this.objectToSerialize = objectToSerialize;
            this.name = name;
            this.ns = ns;
        }
 
        public override bool IsMessageVersionSupported(MessageVersion messageVersion)
        {
            if (messageVersion == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("messageVersion");
            }
 
            if (messageVersion.Envelope == EnvelopeVersion.Soap12)
            {
                return this.isOneTwoSupported;
            }
            else if (messageVersion.Envelope == EnvelopeVersion.Soap11)
            {
                return this.isOneOneSupported;
            }
            else if (messageVersion.Envelope == EnvelopeVersion.None)
            {
                return this.isNoneSupported;
            }
            else
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.EnvelopeVersionUnknown, messageVersion.Envelope.ToString())));
            }
        }
 
        public override string Name
        {
            get { return name; }
        }
 
        public override string Namespace
        {
            get { return ns; }
        }
 
        public override bool MustUnderstand
        {
            get { return mustUnderstand; }
        }
 
        public override bool Relay
        {
            get { return relay; }
        }
 
        public override string Actor
        {
            get { return actor; }
        }
 
        protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
        {
            lock (syncRoot)
            {
                if (serializer == null)
                {
                    serializer = DataContractSerializerDefaults.CreateSerializer(
                        (objectToSerialize == null ? typeof(object) : objectToSerialize.GetType()), this.Name, this.Namespace, int.MaxValue/*maxItems*/);
                }
 
                serializer.WriteObjectContent(writer, objectToSerialize);
            }
        }
    }
 
    abstract class ReadableMessageHeader : MessageHeader
    {
        public abstract XmlDictionaryReader GetHeaderReader();
 
        protected override void OnWriteStartHeader(XmlDictionaryWriter writer, MessageVersion messageVersion)
        {
            if (!IsMessageVersionSupported(messageVersion))
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.MessageHeaderVersionNotSupported, this.GetType().FullName, messageVersion.ToString()), "version"));
            XmlDictionaryReader reader = GetHeaderReader();
            writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
            writer.WriteAttributes(reader, false);
            reader.Close();
        }
 
        protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
        {
            XmlDictionaryReader reader = GetHeaderReader();
            reader.ReadStartElement();
            while (reader.NodeType != XmlNodeType.EndElement)
                writer.WriteNode(reader, false);
            reader.ReadEndElement();
            reader.Close();
        }
    }
 
    internal interface IMessageHeaderWithSharedNamespace
    {
        XmlDictionaryString SharedNamespace { get; }
        XmlDictionaryString SharedPrefix { get; }
    }
 
    class BufferedHeader : ReadableMessageHeader
    {
        MessageVersion version;
        XmlBuffer buffer;
        int bufferIndex;
        string actor;
        bool relay;
        bool mustUnderstand;
        string name;
        string ns;
        bool streamed;
        bool isRefParam;
 
        public BufferedHeader(MessageVersion version, XmlBuffer buffer, int bufferIndex, string name, string ns, bool mustUnderstand, string actor, bool relay, bool isRefParam)
        {
            this.version = version;
            this.buffer = buffer;
            this.bufferIndex = bufferIndex;
            this.name = name;
            this.ns = ns;
            this.mustUnderstand = mustUnderstand;
            this.actor = actor;
            this.relay = relay;
            this.isRefParam = isRefParam;
        }
 
        public BufferedHeader(MessageVersion version, XmlBuffer buffer, int bufferIndex, MessageHeaderInfo headerInfo)
        {
            this.version = version;
            this.buffer = buffer;
            this.bufferIndex = bufferIndex;
            actor = headerInfo.Actor;
            relay = headerInfo.Relay;
            name = headerInfo.Name;
            ns = headerInfo.Namespace;
            isRefParam = headerInfo.IsReferenceParameter;
            mustUnderstand = headerInfo.MustUnderstand;
        }
 
        public BufferedHeader(MessageVersion version, XmlBuffer buffer, XmlDictionaryReader reader, XmlAttributeHolder[] envelopeAttributes, XmlAttributeHolder[] headerAttributes)
        {
            this.streamed = true;
            this.buffer = buffer;
            this.version = version;
            GetHeaderAttributes(reader, version, out this.actor, out this.mustUnderstand, out this.relay, out this.isRefParam);
            name = reader.LocalName;
            ns = reader.NamespaceURI;
            Fx.Assert(name != null, "");
            Fx.Assert(ns != null, "");
            bufferIndex = buffer.SectionCount;
            XmlDictionaryWriter writer = buffer.OpenSection(reader.Quotas);
 
            // Write an enclosing Envelope tag
            writer.WriteStartElement(MessageStrings.Envelope);
            if (envelopeAttributes != null)
                XmlAttributeHolder.WriteAttributes(envelopeAttributes, writer);
 
            // Write and enclosing Header tag
            writer.WriteStartElement(MessageStrings.Header);
            if (headerAttributes != null)
                XmlAttributeHolder.WriteAttributes(headerAttributes, writer);
 
            writer.WriteNode(reader, false);
 
            writer.WriteEndElement();
            writer.WriteEndElement();
 
            buffer.CloseSection();
        }
 
        public override string Actor
        {
            get { return actor; }
        }
 
        public override bool IsReferenceParameter
        {
            get { return isRefParam; }
        }
 
        public override string Name
        {
            get { return name; }
        }
 
        public override string Namespace
        {
            get { return ns; }
        }
 
        public override bool MustUnderstand
        {
            get { return mustUnderstand; }
        }
 
        public override bool Relay
        {
            get { return relay; }
        }
 
        public override bool IsMessageVersionSupported(MessageVersion messageVersion)
        {
            if (messageVersion == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("messageVersion"));
            return messageVersion == this.version;
        }
 
        public override XmlDictionaryReader GetHeaderReader()
        {
            XmlDictionaryReader reader = buffer.GetReader(bufferIndex);
            // See if we need to move past the enclosing envelope/header
            if (this.streamed)
            {
                reader.MoveToContent();
                reader.Read(); // Envelope
                reader.Read(); // Header
                reader.MoveToContent();
            }
            return reader;
        }
    }
}