// 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.Description { using System.Collections.Generic; using System.Globalization; using System.Net; using System.Reflection; using System.Runtime; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Dispatcher; using System.Threading; using Microsoft.Xml; using Microsoft.Xml.Schema; using WsdlNS = System.Web.Services.Description; // the description/metadata "mix-in" public class ServiceMetadataExtension { private const string BaseAddressPattern = "{%BaseAddress%}"; internal abstract class WriteFilter : XmlDictionaryWriter { internal XmlWriter Writer; public abstract WriteFilter CloneWriteFilter(); public override void Close() { this.Writer.Close(); } public override void Flush() { this.Writer.Flush(); } public override string LookupPrefix(string ns) { return this.Writer.LookupPrefix(ns); } public override void WriteBase64(byte[] buffer, int index, int count) { this.Writer.WriteBase64(buffer, index, count); } public override void WriteCData(string text) { this.Writer.WriteCData(text); } public override void WriteCharEntity(char ch) { this.Writer.WriteCharEntity(ch); } public override void WriteChars(char[] buffer, int index, int count) { this.Writer.WriteChars(buffer, index, count); } public override void WriteComment(string text) { this.Writer.WriteComment(text); } public override void WriteDocType(string name, string pubid, string sysid, string subset) { this.Writer.WriteDocType(name, pubid, sysid, subset); } public override void WriteEndAttribute() { this.Writer.WriteEndAttribute(); } public override void WriteEndDocument() { this.Writer.WriteEndDocument(); } public override void WriteEndElement() { this.Writer.WriteEndElement(); } public override void WriteEntityRef(string name) { this.Writer.WriteEntityRef(name); } public override void WriteFullEndElement() { this.Writer.WriteFullEndElement(); } public override void WriteProcessingInstruction(string name, string text) { this.Writer.WriteProcessingInstruction(name, text); } public override void WriteRaw(string data) { this.Writer.WriteRaw(data); } public override void WriteRaw(char[] buffer, int index, int count) { this.Writer.WriteRaw(buffer, index, count); } public override void WriteStartAttribute(string prefix, string localName, string ns) { this.Writer.WriteStartAttribute(prefix, localName, ns); } public override void WriteStartDocument(bool standalone) { this.Writer.WriteStartDocument(standalone); } public override void WriteStartDocument() { this.Writer.WriteStartDocument(); } public override void WriteStartElement(string prefix, string localName, string ns) { this.Writer.WriteStartElement(prefix, localName, ns); } public override WriteState WriteState { get { return this.Writer.WriteState; } } public override void WriteString(string text) { this.Writer.WriteString(text); } public override void WriteSurrogateCharEntity(char lowChar, char highChar) { this.Writer.WriteSurrogateCharEntity(lowChar, highChar); } public override void WriteWhitespace(string ws) { this.Writer.WriteWhitespace(ws); } } private class LocationUpdatingWriter : WriteFilter { private readonly string _oldValue; private readonly string _newValue; // passing null for newValue filters any string with oldValue as a prefix rather than replacing internal LocationUpdatingWriter(string oldValue, string newValue) { _oldValue = oldValue; _newValue = newValue; } public override WriteFilter CloneWriteFilter() { return new LocationUpdatingWriter(_oldValue, _newValue); } public override void WriteString(string text) { if (_newValue != null) text = text.Replace(_oldValue, _newValue); else if (text.StartsWith(_oldValue, StringComparison.Ordinal)) text = String.Empty; base.WriteString(text); } } private class DynamicAddressUpdateWriter : WriteFilter { private readonly string _oldHostName; private readonly string _newHostName; private readonly string _newBaseAddress; private readonly bool _removeBaseAddress; private readonly string _requestScheme; private readonly int _requestPort; private readonly IDictionary<string, int> _updatePortsByScheme; internal DynamicAddressUpdateWriter(Uri listenUri, string requestHost, int requestPort, IDictionary<string, int> updatePortsByScheme, bool removeBaseAddress) : this(listenUri.Host, requestHost, removeBaseAddress, listenUri.Scheme, requestPort, updatePortsByScheme) { _newBaseAddress = UpdateUri(listenUri).ToString(); } private DynamicAddressUpdateWriter(string oldHostName, string newHostName, string newBaseAddress, bool removeBaseAddress, string requestScheme, int requestPort, IDictionary<string, int> updatePortsByScheme) : this(oldHostName, newHostName, removeBaseAddress, requestScheme, requestPort, updatePortsByScheme) { _newBaseAddress = newBaseAddress; } private DynamicAddressUpdateWriter(string oldHostName, string newHostName, bool removeBaseAddress, string requestScheme, int requestPort, IDictionary<string, int> updatePortsByScheme) { _oldHostName = oldHostName; _newHostName = newHostName; _removeBaseAddress = removeBaseAddress; _requestScheme = requestScheme; _requestPort = requestPort; _updatePortsByScheme = updatePortsByScheme; } public override WriteFilter CloneWriteFilter() { return new DynamicAddressUpdateWriter(_oldHostName, _newHostName, _newBaseAddress, _removeBaseAddress, _requestScheme, _requestPort, _updatePortsByScheme); } public override void WriteString(string text) { Uri uri; if (_removeBaseAddress && text.StartsWith(ServiceMetadataExtension.BaseAddressPattern, StringComparison.Ordinal)) { text = string.Empty; } else if (!_removeBaseAddress && text.Contains(ServiceMetadataExtension.BaseAddressPattern)) { text = text.Replace(ServiceMetadataExtension.BaseAddressPattern, _newBaseAddress); } else if (Uri.TryCreate(text, UriKind.Absolute, out uri)) { Uri newUri = UpdateUri(uri); if (newUri != null) { text = newUri.ToString(); } } base.WriteString(text); } public void UpdateUri(ref Uri uri, bool updateBaseAddressOnly = false) { Uri newUri = UpdateUri(uri, updateBaseAddressOnly); if (newUri != null) { uri = newUri; } } private Uri UpdateUri(Uri uri, bool updateBaseAddressOnly = false) { // Ordinal comparison okay: we're filtering for auto-generated URIs which will // always be based off the listenURI, so always match in case if (uri.Host != _oldHostName) { return null; } UriBuilder result = new UriBuilder(uri); result.Host = _newHostName; if (!updateBaseAddressOnly) { int port; if (uri.Scheme == _requestScheme) { port = _requestPort; } else if (!_updatePortsByScheme.TryGetValue(uri.Scheme, out port)) { return null; } result.Port = port; } return result.Uri; } } } }