ServiceMetadataExtension.cs 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. //
  2. // ServiceMetadataExtension.cs
  3. //
  4. // Author:
  5. // Atsushi Enomoto <[email protected]>
  6. // Ankit Jain <[email protected]>
  7. // Gonzalo Paniagua Javier ([email protected])
  8. //
  9. // Copyright (C) 2005 Novell, Inc. http://www.novell.com
  10. //
  11. // Permission is hereby granted, free of charge, to any person obtaining
  12. // a copy of this software and associated documentation files (the
  13. // "Software"), to deal in the Software without restriction, including
  14. // without limitation the rights to use, copy, modify, merge, publish,
  15. // distribute, sublicense, and/or sell copies of the Software, and to
  16. // permit persons to whom the Software is furnished to do so, subject to
  17. // the following conditions:
  18. //
  19. // The above copyright notice and this permission notice shall be
  20. // included in all copies or substantial portions of the Software.
  21. //
  22. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  23. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  24. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  25. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  26. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  27. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  28. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  29. //
  30. using System;
  31. using System.Collections;
  32. using System.Collections.Generic;
  33. using System.Collections.ObjectModel;
  34. using System.Collections.Specialized;
  35. using System.IO;
  36. using System.ServiceModel.Channels;
  37. using System.ServiceModel.Dispatcher;
  38. using System.Web;
  39. using System.Web.Services;
  40. using System.Web.Services.Description;
  41. using System.Xml;
  42. using System.Xml.Schema;
  43. using WSServiceDescription = System.Web.Services.Description.ServiceDescription;
  44. using WSMessage = System.Web.Services.Description.Message;
  45. using SMMessage = System.ServiceModel.Channels.Message;
  46. using WCFBinding = System.ServiceModel.Channels.Binding;
  47. namespace System.ServiceModel.Description
  48. {
  49. public class ServiceMetadataExtension : IExtension<ServiceHostBase>
  50. {
  51. const string ServiceMetadataBehaviorHttpGetBinding = "ServiceMetadataBehaviorHttpGetBinding";
  52. MetadataSet metadata;
  53. ServiceHostBase owner;
  54. Dictionary<Uri, ChannelDispatcherBase> _serviceMetadataChanelDispatchers;
  55. [MonoTODO]
  56. public ServiceMetadataExtension ()
  57. {
  58. }
  59. [MonoTODO]
  60. public MetadataSet Metadata {
  61. get {
  62. if (metadata == null) {
  63. MetadataExporter exporter = new WsdlExporter ();
  64. foreach (ServiceEndpoint ep in owner.Description.Endpoints) {
  65. if (ep.Contract.Name == ServiceMetadataBehavior.MexContractName)
  66. continue;
  67. exporter.ExportEndpoint (ep);
  68. }
  69. metadata = exporter.GetGeneratedMetadata ();
  70. }
  71. return metadata;
  72. }
  73. }
  74. internal ServiceHostBase Owner {
  75. get { return owner; }
  76. }
  77. internal static ServiceMetadataExtension EnsureServiceMetadataExtension (ServiceDescription description, ServiceHostBase serviceHostBase) {
  78. ServiceMetadataExtension sme = serviceHostBase.Extensions.Find<ServiceMetadataExtension> ();
  79. if (sme == null) {
  80. sme = new ServiceMetadataExtension ();
  81. serviceHostBase.Extensions.Add (sme);
  82. }
  83. return sme;
  84. }
  85. internal static void EnsureServiceMetadataHttpChanelDispatcher (ServiceDescription description, ServiceHostBase serviceHostBase, ServiceMetadataExtension sme, Uri uri, WCFBinding binding)
  86. {
  87. EnsureServiceMetadataDispatcher (description, serviceHostBase, sme, uri, binding ?? MetadataExchangeBindings.CreateMexHttpBinding ());
  88. }
  89. internal static void EnsureServiceMetadataHttpsChanelDispatcher (ServiceDescription description, ServiceHostBase serviceHostBase, ServiceMetadataExtension sme, Uri uri, WCFBinding binding)
  90. {
  91. // same as http now.
  92. EnsureServiceMetadataDispatcher (description, serviceHostBase, sme, uri, binding ?? MetadataExchangeBindings.CreateMexHttpsBinding ());
  93. }
  94. static void EnsureServiceMetadataDispatcher (ServiceDescription description, ServiceHostBase serviceHostBase, ServiceMetadataExtension sme, Uri uri, WCFBinding binding)
  95. {
  96. if (sme._serviceMetadataChanelDispatchers == null)
  97. sme._serviceMetadataChanelDispatchers = new Dictionary<Uri, ChannelDispatcherBase> ();
  98. else if (sme._serviceMetadataChanelDispatchers.ContainsKey (uri))
  99. return;
  100. CustomBinding cb = new CustomBinding (binding)
  101. {
  102. Name = ServiceMetadataBehaviorHttpGetBinding,
  103. };
  104. cb.Elements.Find<MessageEncodingBindingElement> ().MessageVersion = MessageVersion.None;
  105. ServiceEndpoint se = new ServiceEndpoint (ContractDescription.GetContract (typeof (IHttpGetHelpPageAndMetadataContract)), cb, new EndpointAddress (uri))
  106. {
  107. ListenUri = uri,
  108. };
  109. ChannelDispatcher channelDispatcher = serviceHostBase.BuildChannelDispatcher (se);
  110. channelDispatcher.Endpoints [0].DispatchRuntime.InstanceContextProvider = new SingletonInstanceContextProvider (new InstanceContext (serviceHostBase, new HttpGetWsdl (sme, uri)));
  111. sme._serviceMetadataChanelDispatchers.Add (uri, channelDispatcher);
  112. serviceHostBase.ChannelDispatchers.Add (channelDispatcher);
  113. }
  114. void IExtension<ServiceHostBase>.Attach (ServiceHostBase owner)
  115. {
  116. this.owner = owner;
  117. }
  118. [MonoTODO]
  119. void IExtension<ServiceHostBase>.Detach (ServiceHostBase owner)
  120. {
  121. throw new NotImplementedException ();
  122. }
  123. }
  124. [ServiceContract (Namespace = "http://schemas.microsoft.com/2006/04/http/metadata")]
  125. interface IHttpGetHelpPageAndMetadataContract
  126. {
  127. [OperationContract (Action = "*", ReplyAction = "*")]
  128. SMMessage Get (SMMessage req);
  129. }
  130. class HttpGetWsdl : IHttpGetHelpPageAndMetadataContract
  131. {
  132. ServiceMetadataExtension metadata_extn;
  133. Uri base_uri;
  134. Dictionary <string,WSServiceDescription> wsdl_documents =
  135. new Dictionary<string, WSServiceDescription> ();
  136. Dictionary <string, XmlSchema> schemas =
  137. new Dictionary<string, XmlSchema> ();
  138. public HttpGetWsdl (ServiceMetadataExtension metadata_extn, Uri base_uri)
  139. {
  140. this.metadata_extn = metadata_extn;
  141. this.base_uri = base_uri;
  142. GetMetadata (metadata_extn.Owner);
  143. }
  144. public SMMessage Get (SMMessage req)
  145. {
  146. HttpRequestMessageProperty prop = (HttpRequestMessageProperty) req.Properties [HttpRequestMessageProperty.Name];
  147. NameValueCollection query_string = CreateQueryString (prop.QueryString);
  148. if (query_string == null || query_string.AllKeys.Length != 1)
  149. return CreateHelpPage (req);
  150. if (query_string [null] == "wsdl") {
  151. WSServiceDescription wsdl = GetWsdl ("wsdl");
  152. if (wsdl != null)
  153. return CreateWsdlMessage (wsdl);
  154. } else if (query_string ["wsdl"] != null) {
  155. WSServiceDescription wsdl = GetWsdl (query_string ["wsdl"]);
  156. if (wsdl != null)
  157. return CreateWsdlMessage (wsdl);
  158. } else if (query_string ["xsd"] != null) {
  159. XmlSchema schema = GetXmlSchema (query_string ["xsd"]);
  160. if (schema != null) {
  161. //FIXME: Is this the correct way?
  162. MemoryStream ms = new MemoryStream ();
  163. schema.Write (ms);
  164. ms.Seek (0, SeekOrigin.Begin);
  165. SMMessage ret = SMMessage.CreateMessage (MessageVersion.None, "", XmlReader.Create (ms));
  166. return ret;
  167. }
  168. }
  169. return CreateHelpPage (req);
  170. }
  171. /* Code from HttpListenerRequest */
  172. NameValueCollection CreateQueryString (string query)
  173. {
  174. NameValueCollection query_string = new NameValueCollection ();
  175. if (query == null || query.Length == 0)
  176. return null;
  177. string [] components = query.Split ('&');
  178. foreach (string kv in components) {
  179. int pos = kv.IndexOf ('=');
  180. if (pos == -1) {
  181. query_string.Add (null, HttpUtility.UrlDecode (kv));
  182. } else {
  183. string key = HttpUtility.UrlDecode (kv.Substring (0, pos));
  184. string val = HttpUtility.UrlDecode (kv.Substring (pos + 1));
  185. query_string.Add (key, val);
  186. }
  187. }
  188. return query_string;
  189. }
  190. SMMessage CreateHelpPage (SMMessage request)
  191. {
  192. //FIXME Check for ServiceDebugBehavior.HttpHelpPage
  193. //else do what? Check
  194. throw new NotImplementedException ();
  195. }
  196. SMMessage CreateWsdlMessage (WSServiceDescription wsdl)
  197. {
  198. MemoryStream ms = new MemoryStream ();
  199. XmlWriter xw = XmlWriter.Create (ms);
  200. WSServiceDescription.Serializer.Serialize (xw, wsdl);
  201. ms.Seek (0, SeekOrigin.Begin);
  202. return SMMessage.CreateMessage (MessageVersion.None, "", XmlReader.Create (ms));
  203. }
  204. void GetMetadata (ServiceHostBase host)
  205. {
  206. MetadataSet metadata = metadata_extn.Metadata;
  207. int xs_i = 0, wsdl_i = 0;
  208. //Dictionary keyed by namespace
  209. StringDictionary wsdl_strings = new StringDictionary ();
  210. StringDictionary xsd_strings = new StringDictionary ();
  211. foreach (MetadataSection section in metadata.MetadataSections) {
  212. string key;
  213. XmlSchema xs = section.Metadata as XmlSchema;
  214. if (xs != null) {
  215. key = String.Format ("xsd{0}", xs_i ++);
  216. schemas [key] = xs;
  217. xsd_strings [xs.TargetNamespace] = key;
  218. continue;
  219. }
  220. WSServiceDescription wsdl = section.Metadata as WSServiceDescription;
  221. if (wsdl == null)
  222. continue;
  223. //if (wsdl.TargetNamespace == "http://tempuri.org/")
  224. if (wsdl.Services.Count > 0)
  225. key = "wsdl";
  226. else
  227. key = String.Format ("wsdl{0}", wsdl_i ++);
  228. wsdl_documents [key] = wsdl;
  229. wsdl_strings [wsdl.TargetNamespace] = key;
  230. }
  231. string base_url = base_uri.ToString ();
  232. foreach (WSServiceDescription wsdl in wsdl_documents.Values) {
  233. foreach (Import import in wsdl.Imports) {
  234. if (!String.IsNullOrEmpty (import.Location))
  235. continue;
  236. import.Location = String.Format ("{0}?wsdl={1}", base_url, wsdl_strings [import.Namespace]);
  237. }
  238. foreach (XmlSchema schema in wsdl.Types.Schemas) {
  239. foreach (XmlSchemaObject obj in schema.Includes) {
  240. XmlSchemaImport imp = obj as XmlSchemaImport;
  241. if (imp == null || imp.SchemaLocation != null)
  242. continue;
  243. imp.SchemaLocation = String.Format ("{0}?xsd={1}", base_url, xsd_strings [imp.Namespace]);
  244. }
  245. }
  246. }
  247. }
  248. WSServiceDescription GetWsdl (string which)
  249. {
  250. WSServiceDescription wsdl;
  251. wsdl_documents.TryGetValue (which, out wsdl);
  252. return wsdl;
  253. }
  254. XmlSchema GetXmlSchema (string which)
  255. {
  256. XmlSchema schema;
  257. schemas.TryGetValue (which, out schema);
  258. return schema;
  259. }
  260. }
  261. }