|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace System.ServiceModel.Channels
{
using System.Collections;
using System.Collections.Generic;
using System.Runtime;
using System.ServiceModel;
using System.ServiceModel.Description;
using Microsoft.Xml;
internal static class ReliableSessionPolicyStrings
{
public const string AcknowledgementInterval = "AcknowledgementInterval";
public const string AtLeastOnce = "AtLeastOnce";
public const string AtMostOnce = "AtMostOnce";
public const string BaseRetransmissionInterval = "BaseRetransmissionInterval";
public const string DeliveryAssurance = "DeliveryAssurance";
public const string ExactlyOnce = "ExactlyOnce";
public const string ExponentialBackoff = "ExponentialBackoff";
public const string InactivityTimeout = "InactivityTimeout";
public const string InOrder = "InOrder";
public const string Milliseconds = "Milliseconds";
public const string NET11Namespace = "http://schemas.microsoft.com/ws-rx/wsrmp/200702";
public const string NET11Prefix = "netrmp";
public const string ReliableSessionName = "RMAssertion";
public const string ReliableSessionFebruary2005Namespace = "http://schemas.xmlsoap.org/ws/2005/02/rm/policy";
public const string ReliableSessionFebruary2005Prefix = "wsrm";
public const string ReliableSession11Namespace = "http://docs.oasis-open.org/ws-rx/wsrmp/200702";
public const string ReliableSession11Prefix = "wsrmp";
public const string SequenceSTR = "SequenceSTR";
public const string SequenceTransportSecurity = "SequenceTransportSecurity";
}
public sealed class ReliableSessionBindingElementImporter : IPolicyImportExtension
{
void IPolicyImportExtension.ImportPolicy(MetadataImporter importer, PolicyConversionContext context)
{
if (importer == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("importer");
}
if (context == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context");
}
bool gotAssertion = false;
XmlElement reliableSessionAssertion = PolicyConversionContext.FindAssertion(context.GetBindingAssertions(),
ReliableSessionPolicyStrings.ReliableSessionName,
ReliableSessionPolicyStrings.ReliableSessionFebruary2005Namespace, true);
if (reliableSessionAssertion != null)
{
ProcessReliableSessionFeb2005Assertion(reliableSessionAssertion, GetReliableSessionBindingElement(context));
gotAssertion = true;
}
reliableSessionAssertion = PolicyConversionContext.FindAssertion(context.GetBindingAssertions(),
ReliableSessionPolicyStrings.ReliableSessionName,
ReliableSessionPolicyStrings.ReliableSession11Namespace, true);
if (reliableSessionAssertion != null)
{
if (gotAssertion)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidChannelBindingException(
string.Format(SRServiceModel.MultipleVersionsFoundInPolicy,
ReliableSessionPolicyStrings.ReliableSessionName)));
}
ProcessReliableSession11Assertion(importer, reliableSessionAssertion,
GetReliableSessionBindingElement(context));
}
}
private static ReliableSessionBindingElement GetReliableSessionBindingElement(PolicyConversionContext context)
{
ReliableSessionBindingElement settings = context.BindingElements.Find<ReliableSessionBindingElement>();
if (settings == null)
{
settings = new ReliableSessionBindingElement();
context.BindingElements.Add(settings);
}
return settings;
}
private static bool Is11Assertion(XmlNode node, string assertion)
{
return IsElement(node, ReliableSessionPolicyStrings.NET11Namespace, assertion);
}
private static bool IsElement(XmlNode node, string ns, string assertion)
{
if (assertion == null)
{
throw Fx.AssertAndThrow("Argument assertion cannot be null.");
}
return ((node != null)
&& (node.NodeType == XmlNodeType.Element)
&& (node.NamespaceURI == ns)
&& (node.LocalName == assertion));
}
private static bool IsFeb2005Assertion(XmlNode node, string assertion)
{
return IsElement(node, ReliableSessionPolicyStrings.ReliableSessionFebruary2005Namespace, assertion);
}
private static void ProcessReliableSession11Assertion(MetadataImporter importer, XmlElement element,
ReliableSessionBindingElement settings)
{
// Version
settings.ReliableMessagingVersion = ReliableMessagingVersion.WSReliableMessaging11;
IEnumerator assertionChildren = element.ChildNodes.GetEnumerator();
XmlNode currentNode = SkipToNode(assertionChildren);
// Policy
ProcessWsrm11Policy(importer, currentNode, settings);
currentNode = SkipToNode(assertionChildren);
// Looking for:
// InactivityTimeout, AcknowledgementInterval
// InactivityTimeout
// AcknowledgementInterval
// or nothing at all.
State state = State.InactivityTimeout;
while (currentNode != null)
{
if (state == State.InactivityTimeout)
{
// InactivityTimeout assertion
if (Is11Assertion(currentNode, ReliableSessionPolicyStrings.InactivityTimeout))
{
SetInactivityTimeout(settings, ReadMillisecondsAttribute(currentNode, true), currentNode.LocalName);
state = State.AcknowledgementInterval;
currentNode = SkipToNode(assertionChildren);
continue;
}
}
// AcknowledgementInterval assertion
if (Is11Assertion(currentNode, ReliableSessionPolicyStrings.AcknowledgementInterval))
{
SetAcknowledgementInterval(settings, ReadMillisecondsAttribute(currentNode, true), currentNode.LocalName);
// ignore the rest
break;
}
if (state == State.AcknowledgementInterval)
{
// ignore the rest
break;
}
currentNode = SkipToNode(assertionChildren);
}
// Schema allows arbitrary elements from now on, ignore everything else
}
private static void ProcessReliableSessionFeb2005Assertion(XmlElement element, ReliableSessionBindingElement settings)
{
// Version
settings.ReliableMessagingVersion = ReliableMessagingVersion.WSReliableMessagingFebruary2005;
IEnumerator nodes = element.ChildNodes.GetEnumerator();
XmlNode currentNode = SkipToNode(nodes);
// InactivityTimeout assertion
if (IsFeb2005Assertion(currentNode, ReliableSessionPolicyStrings.InactivityTimeout))
{
SetInactivityTimeout(settings, ReadMillisecondsAttribute(currentNode, true), currentNode.LocalName);
currentNode = SkipToNode(nodes);
}
// BaseRetransmissionInterval assertion is read but ignored
if (IsFeb2005Assertion(currentNode, ReliableSessionPolicyStrings.BaseRetransmissionInterval))
{
ReadMillisecondsAttribute(currentNode, false);
currentNode = SkipToNode(nodes);
}
// ExponentialBackoff assertion is read but ignored
if (IsFeb2005Assertion(currentNode, ReliableSessionPolicyStrings.ExponentialBackoff))
{
currentNode = SkipToNode(nodes);
}
// AcknowledgementInterval assertion
if (IsFeb2005Assertion(currentNode, ReliableSessionPolicyStrings.AcknowledgementInterval))
{
SetAcknowledgementInterval(settings, ReadMillisecondsAttribute(currentNode, true), currentNode.LocalName);
}
// Schema allows arbitrary elements from now on, ignore everything else
}
private static void ProcessWsrm11Policy(MetadataImporter importer, XmlNode node, ReliableSessionBindingElement settings)
{
XmlElement element = ThrowIfNotPolicyElement(node, ReliableMessagingVersion.WSReliableMessaging11);
IEnumerable<IEnumerable<XmlElement>> alternatives = importer.NormalizePolicy(new XmlElement[] { element });
List<Wsrm11PolicyAlternative> wsrmAlternatives = new List<Wsrm11PolicyAlternative>();
foreach (IEnumerable<XmlElement> alternative in alternatives)
{
Wsrm11PolicyAlternative wsrm11Policy = Wsrm11PolicyAlternative.ImportAlternative(importer, alternative);
wsrmAlternatives.Add(wsrm11Policy);
}
if (wsrmAlternatives.Count == 0)
{
// No specific policy other than turn on WS-RM.
return;
}
foreach (Wsrm11PolicyAlternative wsrmAlternative in wsrmAlternatives)
{
// The only policy setting that affects the binding is the InOrder assurance.
// Even that setting does not affect the binding since InOrder is a server delivery assurance.
// Transfer any that is valid.
if (wsrmAlternative.HasValidPolicy)
{
wsrmAlternative.TransferSettings(settings);
return;
}
}
// Found only invalid policy.
// This throws an exception about security since that is the only invalid policy we have.
Wsrm11PolicyAlternative.ThrowInvalidBindingException();
}
private static TimeSpan ReadMillisecondsAttribute(XmlNode wsrmNode, bool convertToTimeSpan)
{
XmlAttribute millisecondsAttribute = wsrmNode.Attributes[ReliableSessionPolicyStrings.Milliseconds];
if (millisecondsAttribute == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidChannelBindingException(string.Format(SRServiceModel.RequiredAttributeIsMissing, ReliableSessionPolicyStrings.Milliseconds, wsrmNode.LocalName, ReliableSessionPolicyStrings.ReliableSessionName)));
UInt64 milliseconds = 0;
Exception innerException = null;
try
{
milliseconds = XmlConvert.ToUInt64(millisecondsAttribute.Value);
}
catch (FormatException exception)
{
innerException = exception;
}
catch (OverflowException exception)
{
innerException = exception;
}
if (innerException != null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidChannelBindingException(string.Format(SRServiceModel.RequiredMillisecondsAttributeIncorrect, wsrmNode.LocalName), innerException));
if (convertToTimeSpan)
{
TimeSpan interval;
try
{
interval = TimeSpan.FromMilliseconds(Convert.ToDouble(milliseconds));
}
catch (OverflowException exception)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidChannelBindingException(string.Format(SRServiceModel.MillisecondsNotConvertibleToBindingRange, wsrmNode.LocalName), exception));
}
return interval;
}
else
{
return default(TimeSpan);
}
}
private static void SetInactivityTimeout(ReliableSessionBindingElement settings, TimeSpan inactivityTimeout, string localName)
{
try
{
settings.InactivityTimeout = inactivityTimeout;
}
catch (ArgumentOutOfRangeException exception)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidChannelBindingException(string.Format(SRServiceModel.MillisecondsNotConvertibleToBindingRange, localName), exception));
}
}
private static void SetAcknowledgementInterval(ReliableSessionBindingElement settings, TimeSpan acknowledgementInterval, string localName)
{
try
{
settings.AcknowledgementInterval = acknowledgementInterval;
}
catch (ArgumentOutOfRangeException exception)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidChannelBindingException(string.Format(SRServiceModel.MillisecondsNotConvertibleToBindingRange, localName), exception));
}
}
private static bool ShouldSkipNodeType(XmlNodeType type)
{
return (type == XmlNodeType.Comment || type == XmlNodeType.SignificantWhitespace || type == XmlNodeType.Whitespace || type == XmlNodeType.Notation);
}
private static XmlNode SkipToNode(IEnumerator nodes)
{
while (nodes.MoveNext())
{
XmlNode currentNode = (XmlNode)nodes.Current;
if (ShouldSkipNodeType(currentNode.NodeType))
continue;
return currentNode;
}
return null;
}
private static XmlElement ThrowIfNotPolicyElement(XmlNode node, ReliableMessagingVersion reliableMessagingVersion)
{
string policyLocalName = MetadataStrings.WSPolicy.Elements.Policy;
if (!IsElement(node, MetadataStrings.WSPolicy.NamespaceUri, policyLocalName)
&& !IsElement(node, MetadataStrings.WSPolicy.NamespaceUri15, policyLocalName))
{
string wsrmPrefix = (reliableMessagingVersion == ReliableMessagingVersion.WSReliableMessagingFebruary2005)
? ReliableSessionPolicyStrings.ReliableSessionFebruary2005Prefix
: ReliableSessionPolicyStrings.ReliableSession11Prefix;
string exceptionString = (node == null)
? string.Format(SRServiceModel.ElementRequired, wsrmPrefix,
ReliableSessionPolicyStrings.ReliableSessionName, MetadataStrings.WSPolicy.Prefix,
MetadataStrings.WSPolicy.Elements.Policy)
: string.Format(SRServiceModel.ElementFound, wsrmPrefix,
ReliableSessionPolicyStrings.ReliableSessionName, MetadataStrings.WSPolicy.Prefix,
MetadataStrings.WSPolicy.Elements.Policy, node.LocalName, node.NamespaceURI);
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidChannelBindingException(exceptionString));
}
return (XmlElement)node;
}
private class Wsrm11PolicyAlternative
{
private bool _hasValidPolicy = true;
private bool _isOrdered = false;
public bool HasValidPolicy
{
get
{
return _hasValidPolicy;
}
}
public static Wsrm11PolicyAlternative ImportAlternative(MetadataImporter importer,
IEnumerable<XmlElement> alternative)
{
State state = State.Security;
Wsrm11PolicyAlternative wsrmPolicy = new Wsrm11PolicyAlternative();
foreach (XmlElement node in alternative)
{
if (state == State.Security)
{
state = State.DeliveryAssurance;
if (wsrmPolicy.TryImportSequenceSTR(node))
{
continue;
}
}
if (state == State.DeliveryAssurance)
{
state = State.Done;
if (wsrmPolicy.TryImportDeliveryAssurance(importer, node))
{
continue;
}
}
string exceptionString = string.Format(SRServiceModel.UnexpectedXmlChildNode,
node.LocalName,
node.NodeType,
ReliableSessionPolicyStrings.ReliableSessionName);
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidChannelBindingException(exceptionString));
}
return wsrmPolicy;
}
public static void ThrowInvalidBindingException()
{
string exceptionString = string.Format(SRServiceModel.AssertionNotSupported,
ReliableSessionPolicyStrings.ReliableSession11Prefix,
ReliableSessionPolicyStrings.SequenceTransportSecurity);
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidChannelBindingException(exceptionString));
}
public void TransferSettings(ReliableSessionBindingElement settings)
{
settings.Ordered = _isOrdered;
}
private bool TryImportSequenceSTR(XmlElement node)
{
string wsrmNs = ReliableSessionPolicyStrings.ReliableSession11Namespace;
if (IsElement(node, wsrmNs, ReliableSessionPolicyStrings.SequenceSTR))
{
return true;
}
if (IsElement(node, wsrmNs, ReliableSessionPolicyStrings.SequenceTransportSecurity))
{
_hasValidPolicy = false;
return true;
}
return false;
}
private bool TryImportDeliveryAssurance(MetadataImporter importer, XmlElement node)
{
string wsrmNs = ReliableSessionPolicyStrings.ReliableSession11Namespace;
if (!IsElement(node, wsrmNs, ReliableSessionPolicyStrings.DeliveryAssurance))
{
return false;
}
// Policy
IEnumerator policyNodes = node.ChildNodes.GetEnumerator();
XmlNode policyNode = SkipToNode(policyNodes);
XmlElement policyElement = ThrowIfNotPolicyElement(policyNode, ReliableMessagingVersion.WSReliableMessaging11);
IEnumerable<IEnumerable<XmlElement>> alternatives = importer.NormalizePolicy(new XmlElement[] { policyElement });
foreach (IEnumerable<XmlElement> alternative in alternatives)
{
State state = State.Assurance;
foreach (XmlElement element in alternative)
{
if (state == State.Assurance)
{
state = State.Order;
if (!IsElement(element, wsrmNs, ReliableSessionPolicyStrings.ExactlyOnce)
&& !IsElement(element, wsrmNs, ReliableSessionPolicyStrings.AtMostOnce)
&& !IsElement(element, wsrmNs, ReliableSessionPolicyStrings.AtMostOnce))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidChannelBindingException(string.Format(
SRServiceModel.DeliveryAssuranceRequired,
wsrmNs,
element.LocalName,
element.NamespaceURI)));
}
// Found required DeliveryAssurance, ignore the value and skip to InOrder
continue;
}
if (state == State.Order)
{
state = State.Done;
// InOrder
if (IsElement(element, wsrmNs, ReliableSessionPolicyStrings.InOrder))
{
// set ordered
if (!_isOrdered)
{
_isOrdered = true;
}
continue;
}
}
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidChannelBindingException(string.Format(
SRServiceModel.UnexpectedXmlChildNode,
element.LocalName,
element.NodeType,
ReliableSessionPolicyStrings.DeliveryAssurance)));
}
if (state == State.Assurance)
{
string exceptionString = string.Format(SRServiceModel.DeliveryAssuranceRequiredNothingFound, wsrmNs);
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidChannelBindingException(exceptionString));
}
}
policyNode = SkipToNode(policyNodes);
if (policyNode != null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidChannelBindingException(string.Format(
SRServiceModel.UnexpectedXmlChildNode,
policyNode.LocalName,
policyNode.NodeType,
node.LocalName)));
}
return true;
}
}
private enum State
{
Security,
DeliveryAssurance,
Assurance,
Order,
InactivityTimeout,
AcknowledgementInterval,
Done,
}
}
}
|