//------------------------------------------------------------ // 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; } } }