2016-08-03 10:59:49 +00:00
// <copyright>
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
namespace System.ServiceModel.Description
{
using System.Collections.Generic ;
2016-11-10 13:04:39 +00:00
using System.Diagnostics.CodeAnalysis ;
2016-08-03 10:59:49 +00:00
using System.Globalization ;
using System.IO ;
using System.Linq ;
using System.Runtime ;
using System.Text ;
using System.Xml ;
using System.Xml.Schema ;
using System.Xml.Serialization ;
using WsdlNS = System . Web . Services . Description ;
internal static class WsdlHelper
{
public static WsdlNS . ServiceDescription GetSingleWsdl ( MetadataSet metadataSet )
{
if ( metadataSet . MetadataSections . Count < 1 )
{
return null ;
}
List < WsdlNS . ServiceDescription > wsdls = new List < WsdlNS . ServiceDescription > ( ) ;
List < XmlSchema > xsds = new List < XmlSchema > ( ) ;
foreach ( MetadataSection section in metadataSet . MetadataSections )
{
if ( section . Metadata is WsdlNS . ServiceDescription )
{
wsdls . Add ( ( WsdlNS . ServiceDescription ) section . Metadata ) ;
}
if ( section . Metadata is XmlSchema )
{
xsds . Add ( ( XmlSchema ) section . Metadata ) ;
}
}
VerifyContractNamespace ( wsdls ) ;
WsdlNS . ServiceDescription singleWsdl = GetSingleWsdl ( CopyServiceDescriptionCollection ( wsdls ) ) ;
// Inline XML schemas
foreach ( XmlSchema schema in xsds )
{
XmlSchema newSchema = CloneXsd ( schema ) ;
RemoveSchemaLocations ( newSchema ) ;
singleWsdl . Types . Schemas . Add ( newSchema ) ;
}
return singleWsdl ;
}
private static void RemoveSchemaLocations ( XmlSchema schema )
{
foreach ( XmlSchemaObject schemaObject in schema . Includes )
{
XmlSchemaExternal external = schemaObject as XmlSchemaExternal ;
if ( external ! = null )
{
external . SchemaLocation = null ;
}
}
}
private static WsdlNS . ServiceDescription GetSingleWsdl ( List < WsdlNS . ServiceDescription > wsdls )
{
// Use WSDL that has the contracts as the base for single WSDL
WsdlNS . ServiceDescription singleWsdl = wsdls . First ( wsdl = > wsdl . PortTypes . Count > 0 ) ;
if ( singleWsdl = = null )
{
singleWsdl = new WsdlNS . ServiceDescription ( ) ;
}
else
{
singleWsdl . Types . Schemas . Clear ( ) ;
singleWsdl . Imports . Clear ( ) ;
}
Dictionary < XmlQualifiedName , XmlQualifiedName > bindingReferenceChanges = new Dictionary < XmlQualifiedName , XmlQualifiedName > ( ) ;
foreach ( WsdlNS . ServiceDescription wsdl in wsdls )
{
if ( wsdl ! = singleWsdl )
{
MergeWsdl ( singleWsdl , wsdl , bindingReferenceChanges ) ;
}
}
EnsureSingleNamespace ( singleWsdl , bindingReferenceChanges ) ;
return singleWsdl ;
}
private static List < WsdlNS . ServiceDescription > CopyServiceDescriptionCollection ( List < WsdlNS . ServiceDescription > wsdls )
{
List < WsdlNS . ServiceDescription > newWsdls = new List < WsdlNS . ServiceDescription > ( ) ;
foreach ( WsdlNS . ServiceDescription wsdl in wsdls )
{
newWsdls . Add ( CloneWsdl ( wsdl ) ) ;
}
return newWsdls ;
}
private static void MergeWsdl ( WsdlNS . ServiceDescription singleWsdl , WsdlNS . ServiceDescription wsdl , Dictionary < XmlQualifiedName , XmlQualifiedName > bindingReferenceChanges )
{
if ( wsdl . Services . Count > 0 )
{
singleWsdl . Name = wsdl . Name ;
}
foreach ( WsdlNS . Binding binding in wsdl . Bindings )
{
string uniqueBindingName = NamingHelper . GetUniqueName ( binding . Name , WsdlHelper . IsBindingNameUsed , singleWsdl . Bindings ) ;
if ( binding . Name ! = uniqueBindingName )
{
bindingReferenceChanges . Add (
new XmlQualifiedName ( binding . Name , binding . ServiceDescription . TargetNamespace ) ,
new XmlQualifiedName ( uniqueBindingName , singleWsdl . TargetNamespace ) ) ;
UpdatePolicyKeys ( binding , uniqueBindingName , wsdl ) ;
binding . Name = uniqueBindingName ;
}
singleWsdl . Bindings . Add ( binding ) ;
}
foreach ( object extension in wsdl . Extensions )
{
singleWsdl . Extensions . Add ( extension ) ;
}
foreach ( WsdlNS . Message message in wsdl . Messages )
{
singleWsdl . Messages . Add ( message ) ;
}
foreach ( WsdlNS . Service service in wsdl . Services )
{
singleWsdl . Services . Add ( service ) ;
}
foreach ( string warning in wsdl . ValidationWarnings )
{
singleWsdl . ValidationWarnings . Add ( warning ) ;
}
}
private static void UpdatePolicyKeys ( WsdlNS . Binding binding , string newBindingName , WsdlNS . ServiceDescription wsdl )
{
string oldBindingName = binding . Name ;
// policy
IEnumerable < XmlElement > bindingPolicies = FindAllElements ( wsdl . Extensions , MetadataStrings . WSPolicy . Elements . Policy ) ;
string policyIdStringPrefixFormat = "{0}_" ;
foreach ( XmlElement policyElement in bindingPolicies )
{
XmlNode policyId = policyElement . Attributes . GetNamedItem ( MetadataStrings . Wsu . Attributes . Id , MetadataStrings . Wsu . NamespaceUri ) ;
string policyIdString = policyId . Value ;
string policyIdStringWithOldBindingName = string . Format ( CultureInfo . InvariantCulture , policyIdStringPrefixFormat , oldBindingName ) ;
string policyIdStringWithNewBindingName = string . Format ( CultureInfo . InvariantCulture , policyIdStringPrefixFormat , newBindingName ) ;
if ( policyId ! = null & & policyIdString ! = null & & policyIdString . StartsWith ( policyIdStringWithOldBindingName , StringComparison . Ordinal ) )
{
policyId . Value = policyIdStringWithNewBindingName + policyIdString . Substring ( policyIdStringWithOldBindingName . Length ) ;
}
}
// policy reference
UpdatePolicyReference ( binding . Extensions , oldBindingName , newBindingName ) ;
foreach ( WsdlNS . OperationBinding operationBinding in binding . Operations )
{
UpdatePolicyReference ( operationBinding . Extensions , oldBindingName , newBindingName ) ;
if ( operationBinding . Input ! = null )
{
UpdatePolicyReference ( operationBinding . Input . Extensions , oldBindingName , newBindingName ) ;
}
if ( operationBinding . Output ! = null )
{
UpdatePolicyReference ( operationBinding . Output . Extensions , oldBindingName , newBindingName ) ;
}
foreach ( WsdlNS . FaultBinding fault in operationBinding . Faults )
{
UpdatePolicyReference ( fault . Extensions , oldBindingName , newBindingName ) ;
}
}
}
private static void UpdatePolicyReference ( WsdlNS . ServiceDescriptionFormatExtensionCollection extensions , string oldBindingName , string newBindingName )
{
IEnumerable < XmlElement > bindingPolicyReferences = FindAllElements ( extensions , MetadataStrings . WSPolicy . Elements . PolicyReference ) ;
string policyReferencePrefixFormat = "#{0}_" ;
foreach ( XmlElement policyReferenceElement in bindingPolicyReferences )
{
XmlNode policyReference = policyReferenceElement . Attributes . GetNamedItem ( MetadataStrings . WSPolicy . Attributes . URI ) ;
string policyReferenceValue = policyReference . Value ;
string policyReferenceValueWithOldBindingName = string . Format ( CultureInfo . InvariantCulture , policyReferencePrefixFormat , oldBindingName ) ;
string policyReferenceValueWithNewBindingName = string . Format ( CultureInfo . InvariantCulture , policyReferencePrefixFormat , newBindingName ) ;
if ( policyReference ! = null & & policyReferenceValue ! = null & & policyReferenceValue . StartsWith ( policyReferenceValueWithOldBindingName , StringComparison . Ordinal ) )
{
policyReference . Value = policyReferenceValueWithNewBindingName + policyReference . Value . Substring ( policyReferenceValueWithOldBindingName . Length ) ;
}
}
}
private static IEnumerable < XmlElement > FindAllElements ( WsdlNS . ServiceDescriptionFormatExtensionCollection extensions , string elementName )
{
List < XmlElement > policyReferences = new List < XmlElement > ( ) ;
for ( int i = 0 ; i < extensions . Count ; i + + )
{
XmlElement element = extensions [ i ] as XmlElement ;
if ( element ! = null & & element . LocalName = = elementName )
{
policyReferences . Add ( element ) ;
}
}
return policyReferences ;
}
private static void VerifyContractNamespace ( List < WsdlNS . ServiceDescription > wsdls )
{
IEnumerable < WsdlNS . ServiceDescription > contractWsdls = wsdls . Where ( serviceDescription = > serviceDescription . PortTypes . Count > 0 ) ;
if ( contractWsdls . Count ( ) > 1 )
{
IEnumerable < string > namespaces = contractWsdls . Select < WsdlNS . ServiceDescription , string > ( wsdl = > wsdl . TargetNamespace ) ;
string contractNamespaces = string . Join ( ", " , namespaces ) ;
throw DiagnosticUtility . ExceptionUtility . ThrowHelperError ( new NotSupportedException ( SR . GetString ( SR . SingleWsdlNotGenerated , contractNamespaces ) ) ) ;
}
}
private static void EnsureSingleNamespace ( WsdlNS . ServiceDescription wsdl , Dictionary < XmlQualifiedName , XmlQualifiedName > bindingReferenceChanges )
{
string targetNamespace = wsdl . TargetNamespace ;
foreach ( WsdlNS . Binding binding in wsdl . Bindings )
{
if ( binding . Type . Namespace ! = targetNamespace )
{
binding . Type = new XmlQualifiedName ( binding . Type . Name , targetNamespace ) ;
}
}
foreach ( WsdlNS . PortType portType in wsdl . PortTypes )
{
foreach ( WsdlNS . Operation operation in portType . Operations )
{
WsdlNS . OperationInput messageInput = operation . Messages . Input ;
if ( messageInput ! = null & & messageInput . Message . Namespace ! = targetNamespace )
{
messageInput . Message = new XmlQualifiedName ( messageInput . Message . Name , targetNamespace ) ;
}
WsdlNS . OperationOutput messageOutput = operation . Messages . Output ;
if ( messageOutput ! = null & & messageOutput . Message . Namespace ! = targetNamespace )
{
messageOutput . Message = new XmlQualifiedName ( messageOutput . Message . Name , targetNamespace ) ;
}
foreach ( WsdlNS . OperationFault fault in operation . Faults )
{
if ( fault . Message . Namespace ! = targetNamespace )
{
fault . Message = new XmlQualifiedName ( fault . Message . Name , targetNamespace ) ;
}
}
}
}
foreach ( WsdlNS . Service service in wsdl . Services )
{
foreach ( WsdlNS . Port port in service . Ports )
{
XmlQualifiedName newPortBinding ;
if ( bindingReferenceChanges . TryGetValue ( port . Binding , out newPortBinding ) )
{
port . Binding = newPortBinding ;
}
else if ( port . Binding . Namespace ! = targetNamespace )
{
port . Binding = new XmlQualifiedName ( port . Binding . Name , targetNamespace ) ;
}
}
}
}
private static bool IsBindingNameUsed ( string name , object collection )
{
WsdlNS . BindingCollection bindings = ( WsdlNS . BindingCollection ) collection ;
foreach ( WsdlNS . Binding binding in bindings )
{
if ( binding . Name = = name )
{
return true ;
}
}
return false ;
}
private static WsdlNS . ServiceDescription CloneWsdl ( WsdlNS . ServiceDescription originalWsdl )
{
Fx . Assert ( originalWsdl ! = null , "originalWsdl must not be null" ) ;
WsdlNS . ServiceDescription newWsdl ;
using ( MemoryStream memoryStream = new MemoryStream ( ) )
{
originalWsdl . Write ( memoryStream ) ;
memoryStream . Seek ( 0 , SeekOrigin . Begin ) ;
newWsdl = WsdlNS . ServiceDescription . Read ( memoryStream ) ;
}
return newWsdl ;
}
2016-11-10 13:04:39 +00:00
[SuppressMessage("Microsoft.Security.Xml", "CA3054:DoNotAllowDtdOnXmlTextReader")]
[SuppressMessage("Microsoft.Security.Xml", "CA3069:ReviewDtdProcessingAssignment", Justification = "This is trusted server code from the application only. We should allow the customer add dtd.")]
2016-08-03 10:59:49 +00:00
private static XmlSchema CloneXsd ( XmlSchema originalXsd )
{
Fx . Assert ( originalXsd ! = null , "originalXsd must not be null" ) ;
XmlSchema newXsd ;
using ( MemoryStream memoryStream = new MemoryStream ( ) )
{
originalXsd . Write ( memoryStream ) ;
memoryStream . Seek ( 0 , SeekOrigin . Begin ) ;
2016-11-10 13:04:39 +00:00
newXsd = XmlSchema . Read ( new XmlTextReader ( memoryStream ) { DtdProcessing = DtdProcessing . Parse } , null ) ;
2016-08-03 10:59:49 +00:00
}
return newXsd ;
}
}
}