2 // ServiceMetadataExtension.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
6 // Ankit Jain <jankit@novell.com>
7 // Gonzalo Paniagua Javier (gonzalo@novell.com)
9 // Copyright (C) 2005 Novell, Inc. http://www.novell.com
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:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
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.
31 using System.Collections;
32 using System.Collections.Generic;
33 using System.Collections.ObjectModel;
34 using System.Collections.Specialized;
36 using System.ServiceModel.Channels;
37 using System.ServiceModel.Dispatcher;
39 using System.Web.Services;
40 using System.Web.Services.Description;
42 using System.Xml.Schema;
44 using WSServiceDescription = System.Web.Services.Description.ServiceDescription;
45 using WSMessage = System.Web.Services.Description.Message;
46 using SMMessage = System.ServiceModel.Channels.Message;
47 using WCFBinding = System.ServiceModel.Channels.Binding;
49 namespace System.ServiceModel.Description
51 public class ServiceMetadataExtension : IExtension<ServiceHostBase>
53 const string ServiceMetadataBehaviorHttpGetBinding = "ServiceMetadataBehaviorHttpGetBinding";
56 ServiceHostBase owner;
57 Dictionary<Uri, ChannelDispatcherBase> _serviceMetadataChanelDispatchers;
60 public ServiceMetadataExtension ()
65 public MetadataSet Metadata {
67 if (metadata == null) {
68 MetadataExporter exporter = new WsdlExporter ();
69 foreach (ServiceEndpoint ep in owner.Description.Endpoints) {
70 if (ep.Contract.Name == ServiceMetadataBehavior.MexContractName)
73 exporter.ExportEndpoint (ep);
75 metadata = exporter.GetGeneratedMetadata ();
81 internal ServiceHostBase Owner {
85 internal static ServiceMetadataExtension EnsureServiceMetadataExtension (ServiceDescription description, ServiceHostBase serviceHostBase) {
86 ServiceMetadataExtension sme = serviceHostBase.Extensions.Find<ServiceMetadataExtension> ();
88 sme = new ServiceMetadataExtension ();
89 serviceHostBase.Extensions.Add (sme);
94 internal static void EnsureServiceMetadataHttpChanelDispatcher (ServiceDescription description, ServiceHostBase serviceHostBase, ServiceMetadataExtension sme, Uri uri, WCFBinding binding)
96 EnsureServiceMetadataDispatcher (description, serviceHostBase, sme, uri, binding ?? MetadataExchangeBindings.CreateMexHttpBinding ());
99 internal static void EnsureServiceMetadataHttpsChanelDispatcher (ServiceDescription description, ServiceHostBase serviceHostBase, ServiceMetadataExtension sme, Uri uri, WCFBinding binding)
102 EnsureServiceMetadataDispatcher (description, serviceHostBase, sme, uri, binding ?? MetadataExchangeBindings.CreateMexHttpsBinding ());
105 static void EnsureServiceMetadataDispatcher (ServiceDescription description, ServiceHostBase serviceHostBase, ServiceMetadataExtension sme, Uri uri, WCFBinding binding)
107 if (sme._serviceMetadataChanelDispatchers == null)
108 sme._serviceMetadataChanelDispatchers = new Dictionary<Uri, ChannelDispatcherBase> ();
109 else if (sme._serviceMetadataChanelDispatchers.ContainsKey (uri))
112 CustomBinding cb = new CustomBinding (binding)
114 Name = ServiceMetadataBehaviorHttpGetBinding,
116 cb.Elements.Find<MessageEncodingBindingElement> ().MessageVersion = MessageVersion.None;
118 ServiceEndpoint se = new ServiceEndpoint (ContractDescription.GetContract (typeof (IHttpGetHelpPageAndMetadataContract)), cb, new EndpointAddress (uri))
123 ChannelDispatcher channelDispatcher = serviceHostBase.BuildChannelDispatcher (se, new BindingParameterCollection ());
125 channelDispatcher.Endpoints [0].DispatchRuntime.InstanceContextProvider = new SingletonInstanceContextProvider (new InstanceContext (serviceHostBase, new HttpGetWsdl (sme, uri)));
127 sme._serviceMetadataChanelDispatchers.Add (uri, channelDispatcher);
128 serviceHostBase.ChannelDispatchers.Add (channelDispatcher);
131 void IExtension<ServiceHostBase>.Attach (ServiceHostBase owner)
137 void IExtension<ServiceHostBase>.Detach (ServiceHostBase owner)
139 throw new NotImplementedException ();
143 [ServiceContract (Namespace = "http://schemas.microsoft.com/2006/04/http/metadata")]
144 interface IHttpGetHelpPageAndMetadataContract
146 [OperationContract (Action = "*", ReplyAction = "*")]
147 SMMessage Get (SMMessage req);
150 class HttpGetWsdl : IHttpGetHelpPageAndMetadataContract
152 ServiceMetadataExtension metadata_extn;
155 Dictionary <string,WSServiceDescription> wsdl_documents =
156 new Dictionary<string, WSServiceDescription> ();
157 Dictionary <string, XmlSchema> schemas =
158 new Dictionary<string, XmlSchema> ();
160 public HttpGetWsdl (ServiceMetadataExtension metadata_extn, Uri base_uri)
162 this.metadata_extn = metadata_extn;
163 this.base_uri = base_uri;
164 GetMetadata (metadata_extn.Owner);
167 public SMMessage Get (SMMessage req)
169 HttpRequestMessageProperty prop = (HttpRequestMessageProperty) req.Properties [HttpRequestMessageProperty.Name];
171 NameValueCollection query_string = CreateQueryString (prop.QueryString);
172 if (query_string == null || query_string.AllKeys.Length != 1)
173 return CreateHelpPage (req);
175 if (query_string [null] == "wsdl") {
176 WSServiceDescription wsdl = GetWsdl ("wsdl");
178 return CreateWsdlMessage (wsdl);
179 } else if (query_string ["wsdl"] != null) {
180 WSServiceDescription wsdl = GetWsdl (query_string ["wsdl"]);
182 return CreateWsdlMessage (wsdl);
183 } else if (query_string ["xsd"] != null) {
184 XmlSchema schema = GetXmlSchema (query_string ["xsd"]);
185 if (schema != null) {
186 //FIXME: Is this the correct way?
187 MemoryStream ms = new MemoryStream ();
190 ms.Seek (0, SeekOrigin.Begin);
191 SMMessage ret = SMMessage.CreateMessage (MessageVersion.None, "", XmlReader.Create (ms));
197 return CreateHelpPage (req);
200 /* Code from HttpListenerRequest */
201 NameValueCollection CreateQueryString (string query)
203 NameValueCollection query_string = new NameValueCollection ();
204 if (query == null || query.Length == 0)
207 string [] components = query.Split ('&');
208 foreach (string kv in components) {
209 int pos = kv.IndexOf ('=');
211 query_string.Add (null, HttpUtility.UrlDecode (kv));
213 string key = HttpUtility.UrlDecode (kv.Substring (0, pos));
214 string val = HttpUtility.UrlDecode (kv.Substring (pos + 1));
216 query_string.Add (key, val);
223 SMMessage CreateHelpPage (SMMessage request)
225 //FIXME Check for ServiceDebugBehavior.HttpHelpPage
226 //else do what? Check
227 throw new NotImplementedException ();
230 SMMessage CreateWsdlMessage (WSServiceDescription wsdl)
232 MemoryStream ms = new MemoryStream ();
233 XmlWriter xw = XmlWriter.Create (ms);
235 WSServiceDescription.Serializer.Serialize (xw, wsdl);
236 ms.Seek (0, SeekOrigin.Begin);
237 return SMMessage.CreateMessage (MessageVersion.None, "", XmlReader.Create (ms));
240 void GetMetadata (ServiceHostBase host)
242 MetadataSet metadata = metadata_extn.Metadata;
243 int xs_i = 0, wsdl_i = 0;
245 //Dictionary keyed by namespace
246 StringDictionary wsdl_strings = new StringDictionary ();
247 StringDictionary xsd_strings = new StringDictionary ();
249 foreach (MetadataSection section in metadata.MetadataSections) {
252 XmlSchema xs = section.Metadata as XmlSchema;
254 key = String.Format ("xsd{0}", xs_i ++);
256 xsd_strings [xs.TargetNamespace] = key;
260 WSServiceDescription wsdl = section.Metadata as WSServiceDescription;
264 //if (wsdl.TargetNamespace == "http://tempuri.org/")
265 if (wsdl.Services.Count > 0)
268 key = String.Format ("wsdl{0}", wsdl_i ++);
270 wsdl_documents [key] = wsdl;
271 wsdl_strings [wsdl.TargetNamespace] = key;
274 string base_url = base_uri.ToString ();
275 foreach (WSServiceDescription wsdl in wsdl_documents.Values) {
276 foreach (Import import in wsdl.Imports) {
277 if (!String.IsNullOrEmpty (import.Location))
280 import.Location = String.Format ("{0}?wsdl={1}", base_url, wsdl_strings [import.Namespace]);
283 foreach (XmlSchema schema in wsdl.Types.Schemas) {
284 foreach (XmlSchemaObject obj in schema.Includes) {
285 XmlSchemaImport imp = obj as XmlSchemaImport;
286 if (imp == null || imp.SchemaLocation != null)
289 imp.SchemaLocation = String.Format ("{0}?xsd={1}", base_url, xsd_strings [imp.Namespace]);
296 WSServiceDescription GetWsdl (string which)
298 WSServiceDescription wsdl;
299 wsdl_documents.TryGetValue (which, out wsdl);
303 XmlSchema GetXmlSchema (string which)
306 schemas.TryGetValue (which, out schema);