ServiceMetadataExtension.cs 82 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950
  1. //------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //------------------------------------------------------------
  4. namespace System.ServiceModel.Description
  5. {
  6. using System.Collections.Generic;
  7. using System.Globalization;
  8. using System.Net;
  9. using System.Reflection;
  10. using System.Runtime;
  11. using System.ServiceModel;
  12. using System.ServiceModel.Activation;
  13. using System.ServiceModel.Channels;
  14. using System.ServiceModel.Configuration;
  15. using System.ServiceModel.Dispatcher;
  16. using System.Threading;
  17. using System.Xml;
  18. using System.Xml.Schema;
  19. using WsdlNS = System.Web.Services.Description;
  20. // the description/metadata "mix-in"
  21. public class ServiceMetadataExtension : IExtension<ServiceHostBase>
  22. {
  23. const string BaseAddressPattern = "{%BaseAddress%}";
  24. static readonly Uri EmptyUri = new Uri(String.Empty, UriKind.Relative);
  25. static readonly Type[] httpGetSupportedChannels = new Type[] { typeof(IReplyChannel), };
  26. ServiceMetadataBehavior.MetadataExtensionInitializer initializer;
  27. MetadataSet metadata;
  28. WsdlNS.ServiceDescription singleWsdl;
  29. bool isInitialized = false;
  30. bool isSingleWsdlInitialized = false;
  31. Uri externalMetadataLocation;
  32. ServiceHostBase owner;
  33. object syncRoot = new object();
  34. object singleWsdlSyncRoot = new object();
  35. bool mexEnabled = false;
  36. bool httpGetEnabled = false;
  37. bool httpsGetEnabled = false;
  38. bool httpHelpPageEnabled = false;
  39. bool httpsHelpPageEnabled = false;
  40. Uri mexUrl;
  41. Uri httpGetUrl;
  42. Uri httpsGetUrl;
  43. Uri httpHelpPageUrl;
  44. Uri httpsHelpPageUrl;
  45. Binding httpHelpPageBinding;
  46. Binding httpsHelpPageBinding;
  47. Binding httpGetBinding;
  48. Binding httpsGetBinding;
  49. public ServiceMetadataExtension()
  50. : this(null)
  51. {
  52. }
  53. internal ServiceMetadataExtension(ServiceMetadataBehavior.MetadataExtensionInitializer initializer)
  54. {
  55. this.initializer = initializer;
  56. }
  57. internal ServiceMetadataBehavior.MetadataExtensionInitializer Initializer
  58. {
  59. get { return this.initializer; }
  60. set { this.initializer = value; }
  61. }
  62. public MetadataSet Metadata
  63. {
  64. get
  65. {
  66. EnsureInitialized();
  67. return this.metadata;
  68. }
  69. }
  70. public WsdlNS.ServiceDescription SingleWsdl
  71. {
  72. get
  73. {
  74. EnsureSingleWsdlInitialized();
  75. return this.singleWsdl;
  76. }
  77. }
  78. internal Uri ExternalMetadataLocation
  79. {
  80. get { return this.externalMetadataLocation; }
  81. set { this.externalMetadataLocation = value; }
  82. }
  83. internal bool MexEnabled
  84. {
  85. get { return this.mexEnabled; }
  86. set { this.mexEnabled = value; }
  87. }
  88. internal bool HttpGetEnabled
  89. {
  90. get { return this.httpGetEnabled; }
  91. set { this.httpGetEnabled = value; }
  92. }
  93. internal bool HttpsGetEnabled
  94. {
  95. get { return this.httpsGetEnabled; }
  96. set { this.httpsGetEnabled = value; }
  97. }
  98. internal bool HelpPageEnabled
  99. {
  100. get { return this.httpHelpPageEnabled || this.httpsHelpPageEnabled; }
  101. }
  102. internal bool MetadataEnabled
  103. {
  104. get { return this.mexEnabled || this.httpGetEnabled || this.httpsGetEnabled; }
  105. }
  106. internal bool HttpHelpPageEnabled
  107. {
  108. get { return this.httpHelpPageEnabled; }
  109. set { this.httpHelpPageEnabled = value; }
  110. }
  111. internal bool HttpsHelpPageEnabled
  112. {
  113. get { return this.httpsHelpPageEnabled; }
  114. set { this.httpsHelpPageEnabled = value; }
  115. }
  116. internal Uri MexUrl
  117. {
  118. get { return this.mexUrl; }
  119. set { this.mexUrl = value; }
  120. }
  121. internal Uri HttpGetUrl
  122. {
  123. get { return this.httpGetUrl; }
  124. set { this.httpGetUrl = value; }
  125. }
  126. internal Uri HttpsGetUrl
  127. {
  128. get { return this.httpsGetUrl; }
  129. set { this.httpsGetUrl = value; }
  130. }
  131. internal Uri HttpHelpPageUrl
  132. {
  133. get { return this.httpHelpPageUrl; }
  134. set { this.httpHelpPageUrl = value; }
  135. }
  136. internal Uri HttpsHelpPageUrl
  137. {
  138. get { return this.httpsHelpPageUrl; }
  139. set { this.httpsHelpPageUrl = value; }
  140. }
  141. internal Binding HttpHelpPageBinding
  142. {
  143. get { return this.httpHelpPageBinding; }
  144. set { this.httpHelpPageBinding = value; }
  145. }
  146. internal Binding HttpsHelpPageBinding
  147. {
  148. get { return this.httpsHelpPageBinding; }
  149. set { this.httpsHelpPageBinding = value; }
  150. }
  151. internal Binding HttpGetBinding
  152. {
  153. get { return this.httpGetBinding; }
  154. set { this.httpGetBinding = value; }
  155. }
  156. internal Binding HttpsGetBinding
  157. {
  158. get { return this.httpsGetBinding; }
  159. set { this.httpsGetBinding = value; }
  160. }
  161. internal bool UpdateAddressDynamically { get; set; }
  162. // This dictionary should not be mutated after open
  163. internal IDictionary<string, int> UpdatePortsByScheme { get; set; }
  164. internal static bool TryGetHttpHostAndPort(Uri listenUri, Message request, out string host, out int port)
  165. {
  166. host = null;
  167. port = 0;
  168. // Get the host hedaer
  169. object property;
  170. if (!request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out property))
  171. {
  172. return false;
  173. }
  174. HttpRequestMessageProperty httpRequest = property as HttpRequestMessageProperty;
  175. if (httpRequest == null)
  176. {
  177. return false;
  178. }
  179. string hostHeader = httpRequest.Headers[HttpRequestHeader.Host];
  180. if (string.IsNullOrEmpty(hostHeader))
  181. {
  182. return false;
  183. }
  184. // Split and validate the host and port
  185. string hostUriString = string.Concat(listenUri.Scheme, "://", hostHeader);
  186. Uri hostUri;
  187. if (!Uri.TryCreate(hostUriString, UriKind.Absolute, out hostUri))
  188. {
  189. return false;
  190. }
  191. host = hostUri.Host;
  192. port = hostUri.Port;
  193. return true;
  194. }
  195. void EnsureInitialized()
  196. {
  197. if (!this.isInitialized)
  198. {
  199. lock (this.syncRoot)
  200. {
  201. if (!this.isInitialized)
  202. {
  203. if (this.initializer != null)
  204. {
  205. // the following call will initialize this
  206. // it will use the Metadata property to do the initialization
  207. // this will call back into this method, but exit because isInitialized is set.
  208. // if other threads try to call these methods, they will block on the lock
  209. this.metadata = this.initializer.GenerateMetadata();
  210. }
  211. if (this.metadata == null)
  212. {
  213. this.metadata = new MetadataSet();
  214. }
  215. Thread.MemoryBarrier();
  216. this.isInitialized = true;
  217. this.initializer = null;
  218. }
  219. }
  220. }
  221. }
  222. void EnsureSingleWsdlInitialized()
  223. {
  224. if (!this.isSingleWsdlInitialized)
  225. {
  226. lock (this.singleWsdlSyncRoot)
  227. {
  228. if (!this.isSingleWsdlInitialized)
  229. {
  230. // Could throw NotSupportedException if multiple contract namespaces. Let the exception propagate to the dispatcher and show up on the html error page
  231. this.singleWsdl = WsdlHelper.GetSingleWsdl(this.Metadata);
  232. this.isSingleWsdlInitialized = true;
  233. }
  234. }
  235. }
  236. }
  237. void IExtension<ServiceHostBase>.Attach(ServiceHostBase owner)
  238. {
  239. if (owner == null)
  240. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("owner"));
  241. if (this.owner != null)
  242. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.TheServiceMetadataExtensionInstanceCouldNot2_0)));
  243. owner.ThrowIfClosedOrOpened();
  244. this.owner = owner;
  245. }
  246. void IExtension<ServiceHostBase>.Detach(ServiceHostBase owner)
  247. {
  248. if (owner == null)
  249. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("owner");
  250. if (this.owner == null)
  251. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.TheServiceMetadataExtensionInstanceCouldNot3_0)));
  252. if (this.owner != owner)
  253. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("owner", SR.GetString(SR.TheServiceMetadataExtensionInstanceCouldNot4_0));
  254. this.owner.ThrowIfClosedOrOpened();
  255. this.owner = null;
  256. }
  257. static internal ServiceMetadataExtension EnsureServiceMetadataExtension(ServiceDescription description, ServiceHostBase host)
  258. {
  259. ServiceMetadataExtension mex = host.Extensions.Find<ServiceMetadataExtension>();
  260. if (mex == null)
  261. {
  262. mex = new ServiceMetadataExtension();
  263. host.Extensions.Add(mex);
  264. }
  265. return mex;
  266. }
  267. internal ChannelDispatcher EnsureGetDispatcher(Uri listenUri)
  268. {
  269. ChannelDispatcher channelDispatcher = FindGetDispatcher(listenUri);
  270. if (channelDispatcher == null)
  271. {
  272. channelDispatcher = CreateGetDispatcher(listenUri);
  273. owner.ChannelDispatchers.Add(channelDispatcher);
  274. }
  275. return channelDispatcher;
  276. }
  277. internal ChannelDispatcher EnsureGetDispatcher(Uri listenUri, bool isServiceDebugBehavior)
  278. {
  279. ChannelDispatcher channelDispatcher = FindGetDispatcher(listenUri);
  280. Binding binding;
  281. if (channelDispatcher == null)
  282. {
  283. if (listenUri.Scheme == Uri.UriSchemeHttp)
  284. {
  285. if (isServiceDebugBehavior)
  286. {
  287. binding = this.httpHelpPageBinding ?? MetadataExchangeBindings.HttpGet;
  288. }
  289. else
  290. {
  291. binding = this.httpGetBinding ?? MetadataExchangeBindings.HttpGet;
  292. }
  293. }
  294. else if (listenUri.Scheme == Uri.UriSchemeHttps)
  295. {
  296. if (isServiceDebugBehavior)
  297. {
  298. binding = this.httpsHelpPageBinding ?? MetadataExchangeBindings.HttpsGet;
  299. }
  300. else
  301. {
  302. binding = this.httpsGetBinding ?? MetadataExchangeBindings.HttpsGet;
  303. }
  304. }
  305. else
  306. {
  307. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.SFxGetChannelDispatcherDoesNotSupportScheme, typeof(ChannelDispatcher).Name, Uri.UriSchemeHttp, Uri.UriSchemeHttps)));
  308. }
  309. channelDispatcher = CreateGetDispatcher(listenUri, binding);
  310. owner.ChannelDispatchers.Add(channelDispatcher);
  311. }
  312. return channelDispatcher;
  313. }
  314. ChannelDispatcher FindGetDispatcher(Uri listenUri)
  315. {
  316. foreach (ChannelDispatcherBase channelDispatcherBase in owner.ChannelDispatchers)
  317. {
  318. ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;
  319. if (channelDispatcher != null && channelDispatcher.Listener.Uri == listenUri)
  320. {
  321. if (channelDispatcher.Endpoints.Count == 1 &&
  322. channelDispatcher.Endpoints[0].DispatchRuntime.SingletonInstanceContext != null &&
  323. channelDispatcher.Endpoints[0].DispatchRuntime.SingletonInstanceContext.UserObject is HttpGetImpl)
  324. {
  325. return channelDispatcher;
  326. }
  327. }
  328. }
  329. return null;
  330. }
  331. ChannelDispatcher CreateGetDispatcher(Uri listenUri)
  332. {
  333. if (listenUri.Scheme == Uri.UriSchemeHttp)
  334. {
  335. return CreateGetDispatcher(listenUri, MetadataExchangeBindings.HttpGet);
  336. }
  337. else if (listenUri.Scheme == Uri.UriSchemeHttps)
  338. {
  339. return CreateGetDispatcher(listenUri, MetadataExchangeBindings.HttpsGet);
  340. }
  341. else
  342. {
  343. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.SFxGetChannelDispatcherDoesNotSupportScheme, typeof(ChannelDispatcher).Name, Uri.UriSchemeHttp, Uri.UriSchemeHttps)));
  344. }
  345. }
  346. ChannelDispatcher CreateGetDispatcher(Uri listenUri, Binding binding)
  347. {
  348. EndpointAddress address = new EndpointAddress(listenUri);
  349. Uri listenUriBaseAddress = listenUri;
  350. string listenUriRelativeAddress = string.Empty;
  351. //Set up binding parameter collection
  352. BindingParameterCollection parameters = owner.GetBindingParameters();
  353. AspNetEnvironment.Current.AddMetadataBindingParameters(listenUriBaseAddress, owner.Description.Behaviors, parameters);
  354. // find listener for HTTP GET
  355. IChannelListener listener = null;
  356. if (binding.CanBuildChannelListener<IReplyChannel>(parameters))
  357. {
  358. listener = binding.BuildChannelListener<IReplyChannel>(listenUriBaseAddress, listenUriRelativeAddress, parameters);
  359. }
  360. else
  361. {
  362. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.SFxBindingNotSupportedForMetadataHttpGet)));
  363. }
  364. //create dispatchers
  365. ChannelDispatcher channelDispatcher = new ChannelDispatcher(listener, HttpGetImpl.MetadataHttpGetBinding, binding);
  366. channelDispatcher.MessageVersion = binding.MessageVersion;
  367. EndpointDispatcher dispatcher = new EndpointDispatcher(address, HttpGetImpl.ContractName, HttpGetImpl.ContractNamespace, true);
  368. //Add operation
  369. DispatchOperation operationDispatcher = new DispatchOperation(dispatcher.DispatchRuntime, HttpGetImpl.GetMethodName, HttpGetImpl.RequestAction, HttpGetImpl.ReplyAction);
  370. operationDispatcher.Formatter = MessageOperationFormatter.Instance;
  371. MethodInfo methodInfo = typeof(IHttpGetMetadata).GetMethod(HttpGetImpl.GetMethodName);
  372. operationDispatcher.Invoker = new SyncMethodInvoker(methodInfo);
  373. dispatcher.DispatchRuntime.Operations.Add(operationDispatcher);
  374. //wire up dispatchers
  375. HttpGetImpl impl = new HttpGetImpl(this, listener.Uri);
  376. dispatcher.DispatchRuntime.SingletonInstanceContext = new InstanceContext(owner, impl, false);
  377. dispatcher.DispatchRuntime.MessageInspectors.Add(impl);
  378. channelDispatcher.Endpoints.Add(dispatcher);
  379. dispatcher.ContractFilter = new MatchAllMessageFilter();
  380. dispatcher.FilterPriority = 0;
  381. dispatcher.DispatchRuntime.InstanceContextProvider = InstanceContextProviderBase.GetProviderForMode(InstanceContextMode.Single, dispatcher.DispatchRuntime);
  382. channelDispatcher.ServiceThrottle = owner.ServiceThrottle;
  383. ServiceDebugBehavior sdb = owner.Description.Behaviors.Find<ServiceDebugBehavior>();
  384. if (sdb != null)
  385. channelDispatcher.IncludeExceptionDetailInFaults |= sdb.IncludeExceptionDetailInFaults;
  386. ServiceBehaviorAttribute sba = owner.Description.Behaviors.Find<ServiceBehaviorAttribute>();
  387. if (sba != null)
  388. channelDispatcher.IncludeExceptionDetailInFaults |= sba.IncludeExceptionDetailInFaults;
  389. return channelDispatcher;
  390. }
  391. WriteFilter GetWriteFilter(Message request, Uri listenUri, bool removeBaseAddress)
  392. {
  393. WriteFilter result = null;
  394. if (this.UpdateAddressDynamically)
  395. {
  396. // Update address dynamically based on the request URI
  397. result = GetDynamicAddressWriter(request, listenUri, removeBaseAddress);
  398. }
  399. if (result == null)
  400. {
  401. // Just use the statically known listen URI
  402. if (removeBaseAddress)
  403. {
  404. result = new LocationUpdatingWriter(BaseAddressPattern, null);
  405. }
  406. else
  407. {
  408. result = new LocationUpdatingWriter(BaseAddressPattern, listenUri.ToString());
  409. }
  410. }
  411. return result;
  412. }
  413. DynamicAddressUpdateWriter GetDynamicAddressWriter(Message request, Uri listenUri, bool removeBaseAddress)
  414. {
  415. string requestHost;
  416. int requestPort;
  417. if (!TryGetHttpHostAndPort(listenUri, request, out requestHost, out requestPort))
  418. {
  419. if (request.Headers.To == null)
  420. {
  421. return null;
  422. }
  423. requestHost = request.Headers.To.Host;
  424. requestPort = request.Headers.To.Port;
  425. }
  426. // Perf optimization: don't do dynamic update if it would be a no-op.
  427. // Ordinal string comparison is okay; it just means we don't get the perf optimization
  428. // if the listen host and request host are case-insensitively equal.
  429. if (requestHost == listenUri.Host &&
  430. requestPort == listenUri.Port &&
  431. (UpdatePortsByScheme == null || UpdatePortsByScheme.Count == 0))
  432. {
  433. return null;
  434. }
  435. return new DynamicAddressUpdateWriter(
  436. listenUri, requestHost, requestPort, this.UpdatePortsByScheme, removeBaseAddress);
  437. }
  438. internal class MetadataBindingParameter { }
  439. internal class WSMexImpl : IMetadataExchange
  440. {
  441. internal const string MetadataMexBinding = "ServiceMetadataBehaviorMexBinding";
  442. internal const string ContractName = MetadataStrings.WSTransfer.Name;
  443. internal const string ContractNamespace = MetadataStrings.WSTransfer.Namespace;
  444. internal const string GetMethodName = "Get";
  445. internal const string RequestAction = MetadataStrings.WSTransfer.GetAction;
  446. internal const string ReplyAction = MetadataStrings.WSTransfer.GetResponseAction;
  447. ServiceMetadataExtension parent;
  448. MetadataSet metadataLocationSet;
  449. TypedMessageConverter converter;
  450. Uri listenUri;
  451. bool isListeningOnHttps;
  452. internal WSMexImpl(ServiceMetadataExtension parent, bool isListeningOnHttps, Uri listenUri)
  453. {
  454. this.parent = parent;
  455. this.isListeningOnHttps = isListeningOnHttps;
  456. this.listenUri = listenUri;
  457. if (this.parent.ExternalMetadataLocation != null && this.parent.ExternalMetadataLocation != EmptyUri)
  458. {
  459. this.metadataLocationSet = new MetadataSet();
  460. string location = this.GetLocationToReturn();
  461. MetadataSection metadataLocationSection = new MetadataSection(MetadataSection.ServiceDescriptionDialect, null, new MetadataLocation(location));
  462. this.metadataLocationSet.MetadataSections.Add(metadataLocationSection);
  463. }
  464. }
  465. internal bool IsListeningOnHttps
  466. {
  467. get { return this.isListeningOnHttps; }
  468. set { this.isListeningOnHttps = value; }
  469. }
  470. string GetLocationToReturn()
  471. {
  472. Fx.Assert(this.parent.ExternalMetadataLocation != null, "");
  473. Uri location = this.parent.ExternalMetadataLocation;
  474. if (!location.IsAbsoluteUri)
  475. {
  476. Uri httpAddr = parent.owner.GetVia(Uri.UriSchemeHttp, location);
  477. Uri httpsAddr = parent.owner.GetVia(Uri.UriSchemeHttps, location);
  478. if (this.IsListeningOnHttps && httpsAddr != null)
  479. {
  480. location = httpsAddr;
  481. }
  482. else if (httpAddr != null)
  483. {
  484. location = httpAddr;
  485. }
  486. else
  487. {
  488. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("ExternalMetadataLocation", SR.GetString(SR.SFxBadMetadataLocationNoAppropriateBaseAddress, this.parent.ExternalMetadataLocation.OriginalString));
  489. }
  490. }
  491. return location.ToString();
  492. }
  493. MetadataSet GatherMetadata(string dialect, string identifier)
  494. {
  495. if (metadataLocationSet != null)
  496. {
  497. return metadataLocationSet;
  498. }
  499. else
  500. {
  501. MetadataSet metadataSet = new MetadataSet();
  502. foreach (MetadataSection document in parent.Metadata.MetadataSections)
  503. {
  504. if ((dialect == null || dialect == document.Dialect) &&
  505. (identifier == null || identifier == document.Identifier))
  506. metadataSet.MetadataSections.Add(document);
  507. }
  508. return metadataSet;
  509. }
  510. }
  511. public Message Get(Message request)
  512. {
  513. GetResponse response = new GetResponse();
  514. response.Metadata = GatherMetadata(null, null);
  515. response.Metadata.WriteFilter = parent.GetWriteFilter(request, this.listenUri, true);
  516. if (converter == null)
  517. converter = TypedMessageConverter.Create(typeof(GetResponse), ReplyAction);
  518. return converter.ToMessage(response, request.Version);
  519. }
  520. public IAsyncResult BeginGet(Message request, AsyncCallback callback, object state)
  521. {
  522. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
  523. }
  524. public Message EndGet(IAsyncResult result)
  525. {
  526. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
  527. }
  528. }
  529. //If this contract is changed, you may need to change ServiceMetadataExtension.CreateHttpGetDispatcher()
  530. [ServiceContract]
  531. internal interface IHttpGetMetadata
  532. {
  533. [OperationContract(Action = MessageHeaders.WildcardAction, ReplyAction = MessageHeaders.WildcardAction)]
  534. Message Get(Message msg);
  535. }
  536. internal class HttpGetImpl : IHttpGetMetadata, IDispatchMessageInspector
  537. {
  538. const string DiscoToken = "disco token";
  539. const string DiscoQueryString = "disco";
  540. const string WsdlQueryString = "wsdl";
  541. const string XsdQueryString = "xsd";
  542. const string SingleWsdlQueryString = "singleWsdl";
  543. const string HtmlContentType = "text/html; charset=UTF-8";
  544. const string XmlContentType = "text/xml; charset=UTF-8";
  545. const int closeTimeoutInSeconds = 90;
  546. const int maxQueryStringChars = 2048;
  547. internal const string MetadataHttpGetBinding = "ServiceMetadataBehaviorHttpGetBinding";
  548. internal const string ContractName = "IHttpGetHelpPageAndMetadataContract";
  549. internal const string ContractNamespace = "http://schemas.microsoft.com/2006/04/http/metadata";
  550. internal const string GetMethodName = "Get";
  551. internal const string RequestAction = MessageHeaders.WildcardAction;
  552. internal const string ReplyAction = MessageHeaders.WildcardAction;
  553. internal const string HtmlBreak = "<BR/>";
  554. static string[] NoQueries = new string[0];
  555. ServiceMetadataExtension parent;
  556. object sync = new object();
  557. InitializationData initData;
  558. Uri listenUri;
  559. bool helpPageEnabled = false;
  560. bool getWsdlEnabled = false;
  561. internal HttpGetImpl(ServiceMetadataExtension parent, Uri listenUri)
  562. {
  563. this.parent = parent;
  564. this.listenUri = listenUri;
  565. }
  566. public bool HelpPageEnabled
  567. {
  568. get { return this.helpPageEnabled; }
  569. set { this.helpPageEnabled = value; }
  570. }
  571. public bool GetWsdlEnabled
  572. {
  573. get { return this.getWsdlEnabled; }
  574. set { this.getWsdlEnabled = value; }
  575. }
  576. InitializationData GetInitData()
  577. {
  578. if (this.initData == null)
  579. {
  580. lock (this.sync)
  581. {
  582. if (this.initData == null)
  583. {
  584. this.initData = InitializationData.InitializeFrom(parent);
  585. }
  586. }
  587. }
  588. return this.initData;
  589. }
  590. string FindWsdlReference(DynamicAddressUpdateWriter addressUpdater)
  591. {
  592. if (this.parent.ExternalMetadataLocation == null || this.parent.ExternalMetadataLocation == EmptyUri)
  593. {
  594. return null;
  595. }
  596. else
  597. {
  598. Uri location = this.parent.ExternalMetadataLocation;
  599. Uri result = ServiceHost.GetUri(this.listenUri, location);
  600. if (addressUpdater != null)
  601. {
  602. addressUpdater.UpdateUri(ref result);
  603. }
  604. return result.ToString();
  605. }
  606. }
  607. bool TryHandleDocumentationRequest(Message httpGetRequest, string[] queries, out Message replyMessage)
  608. {
  609. replyMessage = null;
  610. if (!this.HelpPageEnabled)
  611. return false;
  612. if (parent.MetadataEnabled)
  613. {
  614. string discoUrl = null;
  615. string wsdlUrl = null;
  616. string httpGetUrl = null;
  617. string singleWsdlUrl = null;
  618. bool linkMetadata = true;
  619. DynamicAddressUpdateWriter addressUpdater = null;
  620. if (parent.UpdateAddressDynamically)
  621. {
  622. addressUpdater = parent.GetDynamicAddressWriter(httpGetRequest, this.listenUri, false);
  623. }
  624. wsdlUrl = FindWsdlReference(addressUpdater);
  625. httpGetUrl = GetHttpGetUrl(addressUpdater);
  626. if (wsdlUrl == null && httpGetUrl != null)
  627. {
  628. wsdlUrl = httpGetUrl + "?" + WsdlQueryString;
  629. singleWsdlUrl = httpGetUrl + "?" + SingleWsdlQueryString;
  630. }
  631. if (httpGetUrl != null)
  632. discoUrl = httpGetUrl + "?" + DiscoQueryString;
  633. if (wsdlUrl == null)
  634. {
  635. wsdlUrl = GetMexUrl(addressUpdater);
  636. linkMetadata = false;
  637. }
  638. replyMessage = new MetadataOnHelpPageMessage(discoUrl, wsdlUrl, singleWsdlUrl, GetInitData().ServiceName, GetInitData().ClientName, linkMetadata);
  639. }
  640. else
  641. {
  642. replyMessage = new MetadataOffHelpPageMessage(GetInitData().ServiceName);
  643. }
  644. AddHttpProperty(replyMessage, HttpStatusCode.OK, HtmlContentType);
  645. return true;
  646. }
  647. string GetHttpGetUrl(DynamicAddressUpdateWriter addressUpdater)
  648. {
  649. Uri result = null;
  650. if (this.listenUri.Scheme == Uri.UriSchemeHttp)
  651. {
  652. if (parent.HttpGetEnabled)
  653. result = parent.HttpGetUrl;
  654. else if (parent.HttpsGetEnabled)
  655. result = parent.HttpsGetUrl;
  656. }
  657. else
  658. {
  659. if (parent.HttpsGetEnabled)
  660. result = parent.HttpsGetUrl;
  661. else if (parent.HttpGetEnabled)
  662. result = parent.HttpGetUrl;
  663. }
  664. if (result != null)
  665. {
  666. if (addressUpdater != null)
  667. {
  668. addressUpdater.UpdateUri(ref result, this.listenUri.Scheme != result.Scheme /*updateBaseAddressOnly*/);
  669. }
  670. return result.ToString();
  671. }
  672. return null;
  673. }
  674. string GetMexUrl(DynamicAddressUpdateWriter addressUpdater)
  675. {
  676. if (parent.MexEnabled)
  677. {
  678. Uri result = parent.MexUrl;
  679. if (addressUpdater != null)
  680. {
  681. addressUpdater.UpdateUri(ref result);
  682. }
  683. return result.ToString();
  684. }
  685. return null;
  686. }
  687. bool TryHandleMetadataRequest(Message httpGetRequest, string[] queries, out Message replyMessage)
  688. {
  689. replyMessage = null;
  690. if (!this.GetWsdlEnabled)
  691. return false;
  692. WriteFilter writeFilter = parent.GetWriteFilter(httpGetRequest, this.listenUri, false);
  693. string query = FindQuery(queries);
  694. if (String.IsNullOrEmpty(query))
  695. {
  696. //if the documentation page is not available return the default wsdl if it exists
  697. if (!this.helpPageEnabled && GetInitData().DefaultWsdl != null)
  698. {
  699. // use the default WSDL
  700. using (httpGetRequest)
  701. {
  702. replyMessage = new ServiceDescriptionMessage(
  703. GetInitData().DefaultWsdl, writeFilter);
  704. AddHttpProperty(replyMessage, HttpStatusCode.OK, XmlContentType);
  705. GetInitData().FixImportAddresses();
  706. return true;
  707. }
  708. }
  709. return false;
  710. }
  711. // try to look the document up in the query table
  712. object doc;
  713. if (GetInitData().TryQueryLookup(query, out doc))
  714. {
  715. using (httpGetRequest)
  716. {
  717. if (doc is WsdlNS.ServiceDescription)
  718. {
  719. replyMessage = new ServiceDescriptionMessage(
  720. (WsdlNS.ServiceDescription)doc, writeFilter);
  721. }
  722. else if (doc is XmlSchema)
  723. {
  724. replyMessage = new XmlSchemaMessage(
  725. ((XmlSchema)doc), writeFilter);
  726. }
  727. else if (doc is string)
  728. {
  729. if (((string)doc) == DiscoToken)
  730. {
  731. replyMessage = CreateDiscoMessage(writeFilter as DynamicAddressUpdateWriter);
  732. }
  733. else
  734. {
  735. Fx.Assert("Bad object in HttpGetImpl docFromQuery table");
  736. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Bad object in HttpGetImpl docFromQuery table")));
  737. }
  738. }
  739. else
  740. {
  741. Fx.Assert("Bad object in HttpGetImpl docFromQuery table");
  742. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Bad object in HttpGetImpl docFromQuery table")));
  743. }
  744. AddHttpProperty(replyMessage, HttpStatusCode.OK, XmlContentType);
  745. GetInitData().FixImportAddresses();
  746. return true;
  747. }
  748. }
  749. // otherwise see if they just wanted ?WSDL
  750. if (String.Compare(query, WsdlQueryString, StringComparison.OrdinalIgnoreCase) == 0)
  751. {
  752. if (GetInitData().DefaultWsdl != null)
  753. {
  754. // use the default WSDL
  755. using (httpGetRequest)
  756. {
  757. replyMessage = new ServiceDescriptionMessage(
  758. GetInitData().DefaultWsdl, writeFilter);
  759. AddHttpProperty(replyMessage, HttpStatusCode.OK, XmlContentType);
  760. GetInitData().FixImportAddresses();
  761. return true;
  762. }
  763. }
  764. // or redirect to an external WSDL
  765. string wsdlReference = FindWsdlReference(writeFilter as DynamicAddressUpdateWriter);
  766. if (wsdlReference != null)
  767. {
  768. replyMessage = CreateRedirectMessage(wsdlReference);
  769. return true;
  770. }
  771. }
  772. // ?singleWSDL
  773. if (String.Compare(query, SingleWsdlQueryString, StringComparison.OrdinalIgnoreCase) == 0)
  774. {
  775. WsdlNS.ServiceDescription singleWSDL = parent.SingleWsdl;
  776. if (singleWSDL != null)
  777. {
  778. using (httpGetRequest)
  779. {
  780. replyMessage = new ServiceDescriptionMessage(
  781. singleWSDL, writeFilter);
  782. AddHttpProperty(replyMessage, HttpStatusCode.OK, XmlContentType);
  783. return true;
  784. }
  785. }
  786. }
  787. // we weren't able to handle the request -- return the documentation page if available
  788. return false;
  789. }
  790. Message CreateDiscoMessage(DynamicAddressUpdateWriter addressUpdater)
  791. {
  792. Uri wsdlUrlBase = this.listenUri;
  793. if (addressUpdater != null)
  794. {
  795. addressUpdater.UpdateUri(ref wsdlUrlBase);
  796. }
  797. string wsdlUrl = wsdlUrlBase.ToString() + "?" + WsdlQueryString;
  798. Uri docUrl = null;
  799. if (this.listenUri.Scheme == Uri.UriSchemeHttp)
  800. {
  801. if (parent.HttpHelpPageEnabled)
  802. docUrl = parent.HttpHelpPageUrl;
  803. else if (parent.HttpsHelpPageEnabled)
  804. docUrl = parent.HttpsGetUrl;
  805. }
  806. else
  807. {
  808. if (parent.HttpsHelpPageEnabled)
  809. docUrl = parent.HttpsHelpPageUrl;
  810. else if (parent.HttpHelpPageEnabled)
  811. docUrl = parent.HttpGetUrl;
  812. }
  813. if (addressUpdater != null)
  814. {
  815. addressUpdater.UpdateUri(ref docUrl);
  816. }
  817. return new DiscoMessage(wsdlUrl, docUrl.ToString());
  818. }
  819. string FindQuery(string[] queries)
  820. {
  821. string query = null;
  822. foreach (string q in queries)
  823. {
  824. int start = (q.Length > 0 && q[0] == '?') ? 1 : 0;
  825. if (String.Compare(q, start, WsdlQueryString, 0, WsdlQueryString.Length, StringComparison.OrdinalIgnoreCase) == 0)
  826. query = q;
  827. else if (String.Compare(q, start, XsdQueryString, 0, XsdQueryString.Length, StringComparison.OrdinalIgnoreCase) == 0)
  828. query = q;
  829. else if (String.Compare(q, start, SingleWsdlQueryString, 0, SingleWsdlQueryString.Length, StringComparison.OrdinalIgnoreCase) == 0)
  830. query = q;
  831. else if (parent.HelpPageEnabled && (String.Compare(q, start, DiscoQueryString, 0, DiscoQueryString.Length, StringComparison.OrdinalIgnoreCase) == 0))
  832. query = q;
  833. }
  834. return query;
  835. }
  836. Message ProcessHttpRequest(Message httpGetRequest)
  837. {
  838. string queryString = httpGetRequest.Properties.Via.Query;
  839. if (queryString.Length > maxQueryStringChars)
  840. return CreateHttpResponseMessage(HttpStatusCode.RequestUriTooLong);
  841. if (queryString.StartsWith("?", StringComparison.OrdinalIgnoreCase))
  842. queryString = queryString.Substring(1);
  843. string[] queries = queryString.Length > 0 ? queryString.Split('&') : NoQueries;
  844. Message replyMessage = null;
  845. if (TryHandleMetadataRequest(httpGetRequest, queries, out replyMessage))
  846. return replyMessage;
  847. if (TryHandleDocumentationRequest(httpGetRequest, queries, out replyMessage))
  848. return replyMessage;
  849. return CreateHttpResponseMessage(HttpStatusCode.MethodNotAllowed);
  850. }
  851. public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
  852. {
  853. return request.Version;
  854. }
  855. public void BeforeSendReply(ref Message reply, object correlationState)
  856. {
  857. if ((reply != null) && reply.IsFault)
  858. {
  859. string error = SR.GetString(SR.SFxInternalServerError);
  860. ExceptionDetail exceptionDetail = null;
  861. MessageFault fault = MessageFault.CreateFault(reply, /* maxBufferSize */ 64 * 1024);
  862. if (fault.HasDetail)
  863. {
  864. exceptionDetail = fault.GetDetail<ExceptionDetail>();
  865. if (exceptionDetail != null)
  866. {
  867. error = SR.GetString(SR.SFxDocExt_Error);
  868. }
  869. }
  870. reply = new MetadataOnHelpPageMessage(error, exceptionDetail);
  871. AddHttpProperty(reply, HttpStatusCode.InternalServerError, HtmlContentType);
  872. }
  873. }
  874. public Message Get(Message message)
  875. {
  876. return ProcessHttpRequest(message);
  877. }
  878. class InitializationData
  879. {
  880. readonly Dictionary<string, object> docFromQuery;
  881. readonly Dictionary<object, string> queryFromDoc;
  882. WsdlNS.ServiceDescriptionCollection wsdls;
  883. XmlSchemaSet xsds;
  884. public string ServiceName;
  885. public string ClientName;
  886. public WsdlNS.ServiceDescription DefaultWsdl;
  887. InitializationData(
  888. Dictionary<string, object> docFromQuery,
  889. Dictionary<object, string> queryFromDoc,
  890. WsdlNS.ServiceDescriptionCollection wsdls,
  891. XmlSchemaSet xsds)
  892. {
  893. this.docFromQuery = docFromQuery;
  894. this.queryFromDoc = queryFromDoc;
  895. this.wsdls = wsdls;
  896. this.xsds = xsds;
  897. }
  898. public bool TryQueryLookup(string query, out object doc)
  899. {
  900. return docFromQuery.TryGetValue(query, out doc);
  901. }
  902. public static InitializationData InitializeFrom(ServiceMetadataExtension extension)
  903. {
  904. Dictionary<string, object> docFromQueryInit = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
  905. Dictionary<object, string> queryFromDocInit = new Dictionary<object, string>();
  906. // this collection type provides useful lookup features
  907. WsdlNS.ServiceDescriptionCollection wsdls = CollectWsdls(extension.Metadata);
  908. XmlSchemaSet xsds = CollectXsds(extension.Metadata);
  909. WsdlNS.ServiceDescription defaultWsdl = null;
  910. WsdlNS.Service someService = GetAnyService(wsdls);
  911. if (someService != null)
  912. defaultWsdl = someService.ServiceDescription;
  913. // WSDLs
  914. {
  915. int i = 0;
  916. foreach (WsdlNS.ServiceDescription wsdlDoc in wsdls)
  917. {
  918. string query = WsdlQueryString;
  919. if (wsdlDoc != defaultWsdl) // don't count the WSDL at ?WSDL
  920. query += "=wsdl" + (i++).ToString(System.Globalization.CultureInfo.InvariantCulture);
  921. docFromQueryInit.Add(query, wsdlDoc);
  922. queryFromDocInit.Add(wsdlDoc, query);
  923. }
  924. }
  925. // XSDs
  926. {
  927. int i = 0;
  928. foreach (XmlSchema xsdDoc in xsds.Schemas())
  929. {
  930. string query = XsdQueryString + "=xsd" + (i++).ToString(System.Globalization.CultureInfo.InvariantCulture);
  931. docFromQueryInit.Add(query, xsdDoc);
  932. queryFromDocInit.Add(xsdDoc, query);
  933. }
  934. }
  935. // Disco
  936. if (extension.HelpPageEnabled)
  937. {
  938. string query = DiscoQueryString;
  939. docFromQueryInit.Add(query, DiscoToken);
  940. queryFromDocInit.Add(DiscoToken, query);
  941. }
  942. InitializationData data = new InitializationData(docFromQueryInit, queryFromDocInit, wsdls, xsds);
  943. data.DefaultWsdl = defaultWsdl;
  944. data.ServiceName = GetAnyWsdlName(wsdls);
  945. data.ClientName = ClientClassGenerator.GetClientClassName(GetAnyContractName(wsdls) ?? "IHello");
  946. return data;
  947. }
  948. static WsdlNS.ServiceDescriptionCollection CollectWsdls(MetadataSet metadata)
  949. {
  950. WsdlNS.ServiceDescriptionCollection wsdls = new WsdlNS.ServiceDescriptionCollection();
  951. foreach (MetadataSection section in metadata.MetadataSections)
  952. {
  953. if (section.Metadata is WsdlNS.ServiceDescription)
  954. {
  955. wsdls.Add((WsdlNS.ServiceDescription)section.Metadata);
  956. }
  957. }
  958. return wsdls;
  959. }
  960. static XmlSchemaSet CollectXsds(MetadataSet metadata)
  961. {
  962. XmlSchemaSet xsds = new XmlSchemaSet();
  963. xsds.XmlResolver = null;
  964. foreach (MetadataSection section in metadata.MetadataSections)
  965. {
  966. if (section.Metadata is XmlSchema)
  967. {
  968. xsds.Add((XmlSchema)section.Metadata);
  969. }
  970. }
  971. return xsds;
  972. }
  973. internal void FixImportAddresses()
  974. {
  975. // fixup imports and includes with addresses
  976. // WSDLs
  977. foreach (WsdlNS.ServiceDescription wsdlDoc in this.wsdls)
  978. {
  979. FixImportAddresses(wsdlDoc);
  980. }
  981. // XSDs
  982. foreach (XmlSchema xsdDoc in this.xsds.Schemas())
  983. {
  984. FixImportAddresses(xsdDoc);
  985. }
  986. }
  987. void FixImportAddresses(WsdlNS.ServiceDescription wsdlDoc)
  988. {
  989. foreach (WsdlNS.Import import in wsdlDoc.Imports)
  990. {
  991. if (!String.IsNullOrEmpty(import.Location)) continue;
  992. WsdlNS.ServiceDescription targetDoc = this.wsdls[import.Namespace ?? String.Empty];
  993. if (targetDoc != null)
  994. {
  995. string query = queryFromDoc[targetDoc];
  996. import.Location = BaseAddressPattern + "?" + query;
  997. }
  998. }
  999. if (wsdlDoc.Types != null)
  1000. {
  1001. foreach (XmlSchema xsdDoc in wsdlDoc.Types.Schemas)
  1002. {
  1003. FixImportAddresses(xsdDoc);
  1004. }
  1005. }
  1006. }
  1007. void FixImportAddresses(XmlSchema xsdDoc)
  1008. {
  1009. foreach (XmlSchemaObject o in xsdDoc.Includes)
  1010. {
  1011. XmlSchemaExternal external = o as XmlSchemaExternal;
  1012. if (external == null || !String.IsNullOrEmpty(external.SchemaLocation)) continue;
  1013. string targetNs = external is XmlSchemaImport ? ((XmlSchemaImport)external).Namespace : xsdDoc.TargetNamespace;
  1014. foreach (XmlSchema targetXsd in this.xsds.Schemas(targetNs ?? String.Empty))
  1015. {
  1016. if (targetXsd != xsdDoc)
  1017. {
  1018. string query = this.queryFromDoc[targetXsd];
  1019. external.SchemaLocation = BaseAddressPattern + "?" + query;
  1020. break;
  1021. }
  1022. }
  1023. }
  1024. }
  1025. static string GetAnyContractName(WsdlNS.ServiceDescriptionCollection wsdls)
  1026. {
  1027. // try to track down a WSDL portType name using a wsdl:service as a starting point
  1028. foreach (WsdlNS.ServiceDescription wsdl in wsdls)
  1029. {
  1030. foreach (WsdlNS.Service service in wsdl.Services)
  1031. {
  1032. foreach (WsdlNS.Port port in service.Ports)
  1033. {
  1034. if (!port.Binding.IsEmpty)
  1035. {
  1036. WsdlNS.Binding binding = wsdls.GetBinding(port.Binding);
  1037. if (!binding.Type.IsEmpty)
  1038. {
  1039. return binding.Type.Name;
  1040. }
  1041. }
  1042. }
  1043. }
  1044. }
  1045. return null;
  1046. }
  1047. static WsdlNS.Service GetAnyService(WsdlNS.ServiceDescriptionCollection wsdls)
  1048. {
  1049. // try to track down a WSDL service
  1050. foreach (WsdlNS.ServiceDescription wsdl in wsdls)
  1051. {
  1052. if (wsdl.Services.Count > 0)
  1053. {
  1054. return wsdl.Services[0];
  1055. }
  1056. }
  1057. return null;
  1058. }
  1059. static string GetAnyWsdlName(WsdlNS.ServiceDescriptionCollection wsdls)
  1060. {
  1061. // try to track down a WSDL name
  1062. foreach (WsdlNS.ServiceDescription wsdl in wsdls)
  1063. {
  1064. if (!String.IsNullOrEmpty(wsdl.Name))
  1065. {
  1066. return wsdl.Name;
  1067. }
  1068. }
  1069. return null;
  1070. }
  1071. }
  1072. #region static helpers
  1073. static void AddHttpProperty(Message message, HttpStatusCode status, string contentType)
  1074. {
  1075. HttpResponseMessageProperty responseProperty = new HttpResponseMessageProperty();
  1076. responseProperty.StatusCode = status;
  1077. responseProperty.Headers.Add(HttpResponseHeader.ContentType, contentType);
  1078. message.Properties.Add(HttpResponseMessageProperty.Name, responseProperty);
  1079. }
  1080. static Message CreateRedirectMessage(string redirectedDestination)
  1081. {
  1082. Message redirectMessage = CreateHttpResponseMessage(HttpStatusCode.RedirectKeepVerb);
  1083. HttpResponseMessageProperty httpResponseProperty = (HttpResponseMessageProperty)redirectMessage.Properties[HttpResponseMessageProperty.Name];
  1084. httpResponseProperty.Headers["Location"] = redirectedDestination;
  1085. return redirectMessage;
  1086. }
  1087. static Message CreateHttpResponseMessage(HttpStatusCode code)
  1088. {
  1089. Message message = new NullMessage();
  1090. HttpResponseMessageProperty httpResponseProperty = new HttpResponseMessageProperty();
  1091. httpResponseProperty.StatusCode = code;
  1092. message.Properties.Add(HttpResponseMessageProperty.Name, httpResponseProperty);
  1093. return message;
  1094. }
  1095. #endregion static helpers
  1096. #region Helper Message implementations
  1097. class DiscoMessage : ContentOnlyMessage
  1098. {
  1099. string wsdlAddress;
  1100. string docAddress;
  1101. public DiscoMessage(string wsdlAddress, string docAddress)
  1102. : base()
  1103. {
  1104. this.wsdlAddress = wsdlAddress;
  1105. this.docAddress = docAddress;
  1106. }
  1107. protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
  1108. {
  1109. writer.WriteStartDocument();
  1110. writer.WriteStartElement("discovery", "http://schemas.xmlsoap.org/disco/");
  1111. writer.WriteStartElement("contractRef", "http://schemas.xmlsoap.org/disco/scl/");
  1112. writer.WriteAttributeString("ref", wsdlAddress);
  1113. writer.WriteAttributeString("docRef", docAddress);
  1114. writer.WriteEndElement(); // </contractRef>
  1115. writer.WriteEndElement(); // </discovery>
  1116. writer.WriteEndDocument();
  1117. }
  1118. }
  1119. class MetadataOnHelpPageMessage : ContentOnlyMessage
  1120. {
  1121. string discoUrl;
  1122. string metadataUrl;
  1123. string singleWsdlUrl;
  1124. string serviceName;
  1125. string clientName;
  1126. bool linkMetadata;
  1127. string errorMessage;
  1128. ExceptionDetail exceptionDetail;
  1129. public MetadataOnHelpPageMessage(string discoUrl, string metadataUrl, string singleWsdlUrl, string serviceName, string clientName, bool linkMetadata)
  1130. : base()
  1131. {
  1132. this.discoUrl = discoUrl;
  1133. this.metadataUrl = metadataUrl;
  1134. this.singleWsdlUrl = singleWsdlUrl;
  1135. this.serviceName = serviceName;
  1136. this.clientName = clientName;
  1137. this.linkMetadata = linkMetadata;
  1138. }
  1139. public MetadataOnHelpPageMessage(string errorMessage, ExceptionDetail exceptionDetail)
  1140. : base()
  1141. {
  1142. this.errorMessage = errorMessage;
  1143. this.exceptionDetail = exceptionDetail;
  1144. }
  1145. protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
  1146. {
  1147. HelpPageWriter page = new HelpPageWriter(writer);
  1148. writer.WriteStartElement("HTML");
  1149. writer.WriteStartElement("HEAD");
  1150. if (!String.IsNullOrEmpty(this.discoUrl))
  1151. {
  1152. page.WriteDiscoLink(this.discoUrl);
  1153. }
  1154. page.WriteStyleSheet();
  1155. page.WriteTitle(!String.IsNullOrEmpty(this.serviceName) ? SR.GetString(SR.SFxDocExt_MainPageTitle, this.serviceName) : SR.GetString(SR.SFxDocExt_MainPageTitleNoServiceName));
  1156. if (!String.IsNullOrEmpty(this.errorMessage))
  1157. {
  1158. page.WriteError(this.errorMessage);
  1159. if (this.exceptionDetail != null)
  1160. {
  1161. page.WriteExceptionDetail(this.exceptionDetail);
  1162. }
  1163. }
  1164. else
  1165. {
  1166. page.WriteToolUsage(this.metadataUrl, this.singleWsdlUrl, this.linkMetadata);
  1167. page.WriteSampleCode(this.clientName);
  1168. }
  1169. writer.WriteEndElement(); // BODY
  1170. writer.WriteEndElement(); // HTML
  1171. }
  1172. struct HelpPageWriter
  1173. {
  1174. XmlWriter writer;
  1175. public HelpPageWriter(XmlWriter writer)
  1176. {
  1177. this.writer = writer;
  1178. }
  1179. internal void WriteClass(string className)
  1180. {
  1181. writer.WriteStartElement("font");
  1182. writer.WriteAttributeString("color", "teal");
  1183. writer.WriteString(className);
  1184. writer.WriteEndElement(); // font
  1185. }
  1186. internal void WriteComment(string comment)
  1187. {
  1188. writer.WriteStartElement("font");
  1189. writer.WriteAttributeString("color", "green");
  1190. writer.WriteString(comment);
  1191. writer.WriteEndElement(); // font
  1192. }
  1193. internal void WriteDiscoLink(string discoUrl)
  1194. {
  1195. writer.WriteStartElement("link");
  1196. writer.WriteAttributeString("rel", "alternate");
  1197. writer.WriteAttributeString("type", "text/xml");
  1198. writer.WriteAttributeString("href", discoUrl);
  1199. writer.WriteEndElement(); // link
  1200. }
  1201. internal void WriteError(string message)
  1202. {
  1203. writer.WriteStartElement("P");
  1204. writer.WriteAttributeString("class", "intro");
  1205. writer.WriteString(message);
  1206. writer.WriteEndElement(); // P
  1207. }
  1208. internal void WriteKeyword(string keyword)
  1209. {
  1210. writer.WriteStartElement("font");
  1211. writer.WriteAttributeString("color", "blue");
  1212. writer.WriteString(keyword);
  1213. writer.WriteEndElement(); // font
  1214. }
  1215. internal void WriteSampleCode(string clientName)
  1216. {
  1217. writer.WriteStartElement("P");
  1218. writer.WriteAttributeString("class", "intro");
  1219. writer.WriteEndElement(); // P
  1220. writer.WriteRaw(SR.GetString(SR.SFxDocExt_MainPageIntro2));
  1221. // C#
  1222. writer.WriteRaw(SR.GetString(SR.SFxDocExt_CS));
  1223. writer.WriteStartElement("PRE");
  1224. WriteKeyword("class ");
  1225. WriteClass("Test\n");
  1226. writer.WriteString("{\n");
  1227. WriteKeyword(" static void ");
  1228. writer.WriteString("Main()\n");
  1229. writer.WriteString(" {\n");
  1230. writer.WriteString(" ");
  1231. WriteClass(clientName);
  1232. writer.WriteString(" client = ");
  1233. WriteKeyword("new ");
  1234. WriteClass(clientName);
  1235. writer.WriteString("();\n\n");
  1236. WriteComment(" // " + SR.GetString(SR.SFxDocExt_MainPageComment) + "\n\n");
  1237. WriteComment(" // " + SR.GetString(SR.SFxDocExt_MainPageComment2) + "\n");
  1238. writer.WriteString(" client.Close();\n");
  1239. writer.WriteString(" }\n");
  1240. writer.WriteString("}\n");
  1241. writer.WriteEndElement(); // PRE
  1242. writer.WriteRaw(HttpGetImpl.HtmlBreak);
  1243. // VB
  1244. writer.WriteRaw(SR.GetString(SR.SFxDocExt_VB));
  1245. writer.WriteStartElement("PRE");
  1246. WriteKeyword("Class ");
  1247. WriteClass("Test\n");
  1248. WriteKeyword(" Shared Sub ");
  1249. writer.WriteString("Main()\n");
  1250. WriteKeyword(" Dim ");
  1251. writer.WriteString("client As ");
  1252. WriteClass(clientName);
  1253. writer.WriteString(" = ");
  1254. WriteKeyword("New ");
  1255. WriteClass(clientName);
  1256. writer.WriteString("()\n");
  1257. WriteComment(" ' " + SR.GetString(SR.SFxDocExt_MainPageComment) + "\n\n");
  1258. WriteComment(" ' " + SR.GetString(SR.SFxDocExt_MainPageComment2) + "\n");
  1259. writer.WriteString(" client.Close()\n");
  1260. WriteKeyword(" End Sub\n");
  1261. WriteKeyword("End Class");
  1262. writer.WriteEndElement(); // PRE
  1263. }
  1264. internal void WriteExceptionDetail(ExceptionDetail exceptionDetail)
  1265. {
  1266. writer.WriteStartElement("PRE");
  1267. writer.WriteString(exceptionDetail.ToString().Replace("\r", ""));
  1268. writer.WriteEndElement(); // PRE
  1269. }
  1270. internal void WriteStyleSheet()
  1271. {
  1272. writer.WriteStartElement("STYLE");
  1273. writer.WriteAttributeString("type", "text/css");
  1274. writer.WriteString("#content{ FONT-SIZE: 0.7em; PADDING-BOTTOM: 2em; MARGIN-LEFT: 30px}");
  1275. writer.WriteString("BODY{MARGIN-TOP: 0px; MARGIN-LEFT: 0px; COLOR: #000000; FONT-FAMILY: Verdana; BACKGROUND-COLOR: white}");
  1276. writer.WriteString("P{MARGIN-TOP: 0px; MARGIN-BOTTOM: 12px; COLOR: #000000; FONT-FAMILY: Verdana}");
  1277. writer.WriteString("PRE{BORDER-RIGHT: #f0f0e0 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #f0f0e0 1px solid; MARGIN-TOP: -5px; PADDING-LEFT: 5px; FONT-SIZE: 1.2em; PADDING-BOTTOM: 5px; BORDER-LEFT: #f0f0e0 1px solid; PADDING-TOP: 5px; BORDER-BOTTOM: #f0f0e0 1px solid; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e5e5cc}");
  1278. writer.WriteString(".heading1{MARGIN-TOP: 0px; PADDING-LEFT: 15px; FONT-WEIGHT: normal; FONT-SIZE: 26px; MARGIN-BOTTOM: 0px; PADDING-BOTTOM: 3px; MARGIN-LEFT: -30px; WIDTH: 100%; COLOR: #ffffff; PADDING-TOP: 10px; FONT-FAMILY: Tahoma; BACKGROUND-COLOR: #003366}");
  1279. writer.WriteString(".intro{MARGIN-LEFT: -15px}");
  1280. writer.WriteEndElement(); // STYLE
  1281. }
  1282. internal void WriteTitle(string title)
  1283. {
  1284. writer.WriteElementString("TITLE", title);
  1285. writer.WriteEndElement(); // HEAD
  1286. writer.WriteStartElement("BODY");
  1287. writer.WriteStartElement("DIV");
  1288. writer.WriteAttributeString("id", "content");
  1289. writer.WriteStartElement("P");
  1290. writer.WriteAttributeString("class", "heading1");
  1291. writer.WriteString(title);
  1292. writer.WriteEndElement(); // P
  1293. writer.WriteRaw(HttpGetImpl.HtmlBreak);
  1294. }
  1295. internal void WriteToolUsage(string wsdlUrl, string singleWsdlUrl, bool linkMetadata)
  1296. {
  1297. writer.WriteStartElement("P");
  1298. writer.WriteAttributeString("class", "intro");
  1299. if (wsdlUrl != null)
  1300. {
  1301. WriteMetadataAddress(SR.SFxDocExt_MainPageIntro1a, "svcutil.exe ", wsdlUrl, linkMetadata);
  1302. if (singleWsdlUrl != null)
  1303. {
  1304. // ?singleWsdl message
  1305. writer.WriteStartElement("P");
  1306. WriteMetadataAddress(SR.SFxDocExt_MainPageIntroSingleWsdl, null, singleWsdlUrl, linkMetadata);
  1307. writer.WriteEndElement();
  1308. }
  1309. }
  1310. else
  1311. {
  1312. // no metadata message
  1313. writer.WriteRaw(SR.GetString(SR.SFxDocExt_MainPageIntro1b));
  1314. }
  1315. writer.WriteEndElement(); // P
  1316. }
  1317. void WriteMetadataAddress(string introductionText, string clientToolName, string wsdlUrl, bool linkMetadata)
  1318. {
  1319. writer.WriteRaw(SR.GetString(introductionText));
  1320. writer.WriteRaw(HttpGetImpl.HtmlBreak);
  1321. writer.WriteStartElement("PRE");
  1322. if (!string.IsNullOrEmpty(clientToolName))
  1323. {
  1324. writer.WriteString(clientToolName);
  1325. }
  1326. if (linkMetadata)
  1327. {
  1328. writer.WriteStartElement("A");
  1329. writer.WriteAttributeString("HREF", wsdlUrl);
  1330. }
  1331. writer.WriteString(wsdlUrl);
  1332. if (linkMetadata)
  1333. {
  1334. writer.WriteEndElement(); // A
  1335. }
  1336. writer.WriteEndElement(); // PRE
  1337. }
  1338. }
  1339. }
  1340. class MetadataOffHelpPageMessage : ContentOnlyMessage
  1341. {
  1342. public MetadataOffHelpPageMessage(string serviceName)
  1343. {
  1344. }
  1345. protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
  1346. {
  1347. writer.WriteStartElement("HTML");
  1348. writer.WriteStartElement("HEAD");
  1349. writer.WriteRaw(String.Format(CultureInfo.InvariantCulture,
  1350. @"<STYLE type=""text/css"">#content{{ FONT-SIZE: 0.7em; PADDING-BOTTOM: 2em; MARGIN-LEFT: 30px}}BODY{{MARGIN-TOP: 0px; MARGIN-LEFT: 0px; COLOR: #000000; FONT-FAMILY: Verdana; BACKGROUND-COLOR: white}}P{{MARGIN-TOP: 0px; MARGIN-BOTTOM: 12px; COLOR: #000000; FONT-FAMILY: Verdana}}PRE{{BORDER-RIGHT: #f0f0e0 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #f0f0e0 1px solid; MARGIN-TOP: -5px; PADDING-LEFT: 5px; FONT-SIZE: 1.2em; PADDING-BOTTOM: 5px; BORDER-LEFT: #f0f0e0 1px solid; PADDING-TOP: 5px; BORDER-BOTTOM: #f0f0e0 1px solid; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e5e5cc}}.heading1{{MARGIN-TOP: 0px; PADDING-LEFT: 15px; FONT-WEIGHT: normal; FONT-SIZE: 26px; MARGIN-BOTTOM: 0px; PADDING-BOTTOM: 3px; MARGIN-LEFT: -30px; WIDTH: 100%; COLOR: #ffffff; PADDING-TOP: 10px; FONT-FAMILY: Tahoma; BACKGROUND-COLOR: #003366}}.intro{{MARGIN-LEFT: -15px}}</STYLE>
  1351. <TITLE>Service</TITLE>"));
  1352. writer.WriteEndElement(); //HEAD
  1353. writer.WriteRaw(String.Format(CultureInfo.InvariantCulture,
  1354. @"<BODY>
  1355. <DIV id=""content"">
  1356. <P class=""heading1"">Service</P>
  1357. <BR/>
  1358. <P class=""intro"">{0}</P>
  1359. <PRE>
  1360. <font color=""blue"">&lt;<font color=""darkred"">" + ConfigurationStrings.BehaviorsSectionName + @"</font>&gt;</font>
  1361. <font color=""blue""> &lt;<font color=""darkred"">" + ConfigurationStrings.ServiceBehaviors + @"</font>&gt;</font>
  1362. <font color=""blue""> &lt;<font color=""darkred"">" + ConfigurationStrings.Behavior + @" </font><font color=""red"">" + ConfigurationStrings.Name + @"</font>=<font color=""black"">""</font>MyServiceTypeBehaviors<font color=""black"">"" </font>&gt;</font>
  1363. <font color=""blue""> &lt;<font color=""darkred"">" + ConfigurationStrings.ServiceMetadataPublishingSectionName + @" </font><font color=""red"">" + ConfigurationStrings.HttpGetEnabled + @"</font>=<font color=""black"">""</font>true<font color=""black"">"" </font>/&gt;</font>
  1364. <font color=""blue""> &lt;<font color=""darkred"">/" + ConfigurationStrings.Behavior + @"</font>&gt;</font>
  1365. <font color=""blue""> &lt;<font color=""darkred"">/" + ConfigurationStrings.ServiceBehaviors + @"</font>&gt;</font>
  1366. <font color=""blue"">&lt;<font color=""darkred"">/" + ConfigurationStrings.BehaviorsSectionName + @"</font>&gt;</font>
  1367. </PRE>
  1368. <P class=""intro"">{1}</P>
  1369. <PRE>
  1370. <font color=""blue"">&lt;<font color=""darkred"">" + ConfigurationStrings.Service + @" </font><font color=""red"">" + ConfigurationStrings.Name + @"</font>=<font color=""black"">""</font><i>MyNamespace.MyServiceType</i><font color=""black"">"" </font><font color=""red"">" + ConfigurationStrings.BehaviorConfiguration + @"</font>=<font color=""black"">""</font><i>MyServiceTypeBehaviors</i><font color=""black"">"" </font>&gt;</font>
  1371. </PRE>
  1372. <P class=""intro"">{2}</P>
  1373. <PRE>
  1374. <font color=""blue"">&lt;<font color=""darkred"">" + ConfigurationStrings.Endpoint + @" </font><font color=""red"">" + ConfigurationStrings.Contract + @"</font>=<font color=""black"">""</font>" + ServiceMetadataBehavior.MexContractName + @"<font color=""black"">"" </font><font color=""red"">" + ConfigurationStrings.Binding + @"</font>=<font color=""black"">""</font>mexHttpBinding<font color=""black"">"" </font><font color=""red"">" + ConfigurationStrings.Address + @"</font>=<font color=""black"">""</font>mex<font color=""black"">"" </font>/&gt;</font>
  1375. </PRE>
  1376. <P class=""intro"">{3}</P>
  1377. <PRE>
  1378. <font color=""blue"">&lt;<font color=""darkred"">configuration</font>&gt;</font>
  1379. <font color=""blue""> &lt;<font color=""darkred"">" + ConfigurationStrings.SectionGroupName + @"</font>&gt;</font>
  1380. <font color=""blue""> &lt;<font color=""darkred"">" + ConfigurationStrings.ServicesSectionName + @"</font>&gt;</font>
  1381. <font color=""blue""> &lt;!-- <font color=""green"">{4}</font> --&gt;</font>
  1382. <font color=""blue""> &lt;<font color=""darkred"">" + ConfigurationStrings.Service + @" </font><font color=""red"">" + ConfigurationStrings.Name + @"</font>=<font color=""black"">""</font><i>MyNamespace.MyServiceType</i><font color=""black"">"" </font><font color=""red"">" + ConfigurationStrings.BehaviorConfiguration + @"</font>=<font color=""black"">""</font><i>MyServiceTypeBehaviors</i><font color=""black"">"" </font>&gt;</font>
  1383. <font color=""blue""> &lt;!-- <font color=""green"">{5}</font> --&gt;</font>
  1384. <font color=""blue""> &lt;!-- <font color=""green"">{6}</font> --&gt;</font>
  1385. <font color=""blue""> &lt;<font color=""darkred"">" + ConfigurationStrings.Endpoint + @" </font><font color=""red"">" + ConfigurationStrings.Contract + @"</font>=<font color=""black"">""</font>" + ServiceMetadataBehavior.MexContractName + @"<font color=""black"">"" </font><font color=""red"">" + ConfigurationStrings.Binding + @"</font>=<font color=""black"">""</font>mexHttpBinding<font color=""black"">"" </font><font color=""red"">" + ConfigurationStrings.Address + @"</font>=<font color=""black"">""</font>mex<font color=""black"">"" </font>/&gt;</font>
  1386. <font color=""blue""> &lt;<font color=""darkred"">/" + ConfigurationStrings.Service + @"</font>&gt;</font>
  1387. <font color=""blue""> &lt;<font color=""darkred"">/" + ConfigurationStrings.ServicesSectionName + @"</font>&gt;</font>
  1388. <font color=""blue""> &lt;<font color=""darkred"">" + ConfigurationStrings.BehaviorsSectionName + @"</font>&gt;</font>
  1389. <font color=""blue""> &lt;<font color=""darkred"">" + ConfigurationStrings.ServiceBehaviors + @"</font>&gt;</font>
  1390. <font color=""blue""> &lt;<font color=""darkred"">" + ConfigurationStrings.Behavior + @" </font><font color=""red"">name</font>=<font color=""black"">""</font><i>MyServiceTypeBehaviors</i><font color=""black"">"" </font>&gt;</font>
  1391. <font color=""blue""> &lt;!-- <font color=""green"">{7}</font> --&gt;</font>
  1392. <font color=""blue""> &lt;<font color=""darkred"">" + ConfigurationStrings.ServiceMetadataPublishingSectionName + @" </font><font color=""red"">" + ConfigurationStrings.HttpGetEnabled + @"</font>=<font color=""black"">""</font>true<font color=""black"">"" </font>/&gt;</font>
  1393. <font color=""blue""> &lt;<font color=""darkred"">/" + ConfigurationStrings.Behavior + @"</font>&gt;</font>
  1394. <font color=""blue""> &lt;<font color=""darkred"">/" + ConfigurationStrings.ServiceBehaviors + @"</font>&gt;</font>
  1395. <font color=""blue""> &lt;<font color=""darkred"">/" + ConfigurationStrings.BehaviorsSectionName + @"</font>&gt;</font>
  1396. <font color=""blue""> &lt;<font color=""darkred"">/" + ConfigurationStrings.SectionGroupName + @"</font>&gt;</font>
  1397. <font color=""blue"">&lt;<font color=""darkred"">/configuration</font>&gt;</font>
  1398. </PRE>
  1399. <P class=""intro"">{8}</P>
  1400. </DIV>
  1401. </BODY>",
  1402. SR.GetString(SR.SFxDocExt_NoMetadataSection1), SR.GetString(SR.SFxDocExt_NoMetadataSection2),
  1403. SR.GetString(SR.SFxDocExt_NoMetadataSection3), SR.GetString(SR.SFxDocExt_NoMetadataSection4),
  1404. SR.GetString(SR.SFxDocExt_NoMetadataConfigComment1), SR.GetString(SR.SFxDocExt_NoMetadataConfigComment2),
  1405. SR.GetString(SR.SFxDocExt_NoMetadataConfigComment3), SR.GetString(SR.SFxDocExt_NoMetadataConfigComment4),
  1406. SR.GetString(SR.SFxDocExt_NoMetadataSection5)
  1407. ));
  1408. writer.WriteEndElement(); //HTML
  1409. }
  1410. }
  1411. class ServiceDescriptionMessage : ContentOnlyMessage
  1412. {
  1413. WsdlNS.ServiceDescription description;
  1414. WriteFilter responseWriter;
  1415. public ServiceDescriptionMessage(WsdlNS.ServiceDescription description, WriteFilter responseWriter)
  1416. : base()
  1417. {
  1418. this.description = description;
  1419. this.responseWriter = responseWriter;
  1420. }
  1421. protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
  1422. {
  1423. this.responseWriter.Writer = writer;
  1424. description.Write(this.responseWriter);
  1425. }
  1426. }
  1427. class XmlSchemaMessage : ContentOnlyMessage
  1428. {
  1429. XmlSchema schema;
  1430. WriteFilter responseWriter;
  1431. public XmlSchemaMessage(XmlSchema schema, WriteFilter responseWriter)
  1432. : base()
  1433. {
  1434. this.schema = schema;
  1435. this.responseWriter = responseWriter;
  1436. }
  1437. protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
  1438. {
  1439. this.responseWriter.Writer = writer;
  1440. schema.Write(responseWriter);
  1441. }
  1442. }
  1443. #endregion //Helper Message implementations
  1444. }
  1445. internal abstract class WriteFilter : XmlDictionaryWriter
  1446. {
  1447. internal XmlWriter Writer;
  1448. public abstract WriteFilter CloneWriteFilter();
  1449. public override void Close()
  1450. {
  1451. this.Writer.Close();
  1452. }
  1453. public override void Flush()
  1454. {
  1455. this.Writer.Flush();
  1456. }
  1457. public override string LookupPrefix(string ns)
  1458. {
  1459. return this.Writer.LookupPrefix(ns);
  1460. }
  1461. public override void WriteBase64(byte[] buffer, int index, int count)
  1462. {
  1463. this.Writer.WriteBase64(buffer, index, count);
  1464. }
  1465. public override void WriteCData(string text)
  1466. {
  1467. this.Writer.WriteCData(text);
  1468. }
  1469. public override void WriteCharEntity(char ch)
  1470. {
  1471. this.Writer.WriteCharEntity(ch);
  1472. }
  1473. public override void WriteChars(char[] buffer, int index, int count)
  1474. {
  1475. this.Writer.WriteChars(buffer, index, count);
  1476. }
  1477. public override void WriteComment(string text)
  1478. {
  1479. this.Writer.WriteComment(text);
  1480. }
  1481. public override void WriteDocType(string name, string pubid, string sysid, string subset)
  1482. {
  1483. this.Writer.WriteDocType(name, pubid, sysid, subset);
  1484. }
  1485. public override void WriteEndAttribute()
  1486. {
  1487. this.Writer.WriteEndAttribute();
  1488. }
  1489. public override void WriteEndDocument()
  1490. {
  1491. this.Writer.WriteEndDocument();
  1492. }
  1493. public override void WriteEndElement()
  1494. {
  1495. this.Writer.WriteEndElement();
  1496. }
  1497. public override void WriteEntityRef(string name)
  1498. {
  1499. this.Writer.WriteEntityRef(name);
  1500. }
  1501. public override void WriteFullEndElement()
  1502. {
  1503. this.Writer.WriteFullEndElement();
  1504. }
  1505. public override void WriteProcessingInstruction(string name, string text)
  1506. {
  1507. this.Writer.WriteProcessingInstruction(name, text);
  1508. }
  1509. public override void WriteRaw(string data)
  1510. {
  1511. this.Writer.WriteRaw(data);
  1512. }
  1513. public override void WriteRaw(char[] buffer, int index, int count)
  1514. {
  1515. this.Writer.WriteRaw(buffer, index, count);
  1516. }
  1517. public override void WriteStartAttribute(string prefix, string localName, string ns)
  1518. {
  1519. this.Writer.WriteStartAttribute(prefix, localName, ns);
  1520. }
  1521. public override void WriteStartDocument(bool standalone)
  1522. {
  1523. this.Writer.WriteStartDocument(standalone);
  1524. }
  1525. public override void WriteStartDocument()
  1526. {
  1527. this.Writer.WriteStartDocument();
  1528. }
  1529. public override void WriteStartElement(string prefix, string localName, string ns)
  1530. {
  1531. this.Writer.WriteStartElement(prefix, localName, ns);
  1532. }
  1533. public override WriteState WriteState
  1534. {
  1535. get { return this.Writer.WriteState; }
  1536. }
  1537. public override void WriteString(string text)
  1538. {
  1539. this.Writer.WriteString(text);
  1540. }
  1541. public override void WriteSurrogateCharEntity(char lowChar, char highChar)
  1542. {
  1543. this.Writer.WriteSurrogateCharEntity(lowChar, highChar);
  1544. }
  1545. public override void WriteWhitespace(string ws)
  1546. {
  1547. this.Writer.WriteWhitespace(ws);
  1548. }
  1549. }
  1550. class LocationUpdatingWriter : WriteFilter
  1551. {
  1552. readonly string oldValue;
  1553. readonly string newValue;
  1554. // passing null for newValue filters any string with oldValue as a prefix rather than replacing
  1555. internal LocationUpdatingWriter(string oldValue, string newValue)
  1556. {
  1557. this.oldValue = oldValue;
  1558. this.newValue = newValue;
  1559. }
  1560. public override WriteFilter CloneWriteFilter()
  1561. {
  1562. return new LocationUpdatingWriter(oldValue, newValue);
  1563. }
  1564. public override void WriteString(string text)
  1565. {
  1566. if (this.newValue != null)
  1567. text = text.Replace(this.oldValue, this.newValue);
  1568. else if (text.StartsWith(this.oldValue, StringComparison.Ordinal))
  1569. text = String.Empty;
  1570. base.WriteString(text);
  1571. }
  1572. }
  1573. class DynamicAddressUpdateWriter : WriteFilter
  1574. {
  1575. readonly string oldHostName;
  1576. readonly string newHostName;
  1577. readonly string newBaseAddress;
  1578. readonly bool removeBaseAddress;
  1579. readonly string requestScheme;
  1580. readonly int requestPort;
  1581. readonly IDictionary<string, int> updatePortsByScheme;
  1582. internal DynamicAddressUpdateWriter(Uri listenUri, string requestHost, int requestPort,
  1583. IDictionary<string, int> updatePortsByScheme, bool removeBaseAddress)
  1584. : this(listenUri.Host, requestHost, removeBaseAddress, listenUri.Scheme, requestPort, updatePortsByScheme)
  1585. {
  1586. this.newBaseAddress = UpdateUri(listenUri).ToString();
  1587. }
  1588. DynamicAddressUpdateWriter(string oldHostName, string newHostName, string newBaseAddress, bool removeBaseAddress, string requestScheme,
  1589. int requestPort, IDictionary<string, int> updatePortsByScheme)
  1590. : this(oldHostName, newHostName, removeBaseAddress, requestScheme, requestPort, updatePortsByScheme)
  1591. {
  1592. this.newBaseAddress = newBaseAddress;
  1593. }
  1594. DynamicAddressUpdateWriter(string oldHostName, string newHostName, bool removeBaseAddress, string requestScheme,
  1595. int requestPort, IDictionary<string, int> updatePortsByScheme)
  1596. {
  1597. this.oldHostName = oldHostName;
  1598. this.newHostName = newHostName;
  1599. this.removeBaseAddress = removeBaseAddress;
  1600. this.requestScheme = requestScheme;
  1601. this.requestPort = requestPort;
  1602. this.updatePortsByScheme = updatePortsByScheme;
  1603. }
  1604. public override WriteFilter CloneWriteFilter()
  1605. {
  1606. return new DynamicAddressUpdateWriter(this.oldHostName, this.newHostName, this.newBaseAddress, this.removeBaseAddress,
  1607. this.requestScheme, this.requestPort, this.updatePortsByScheme);
  1608. }
  1609. public override void WriteString(string text)
  1610. {
  1611. Uri uri;
  1612. if (this.removeBaseAddress &&
  1613. text.StartsWith(ServiceMetadataExtension.BaseAddressPattern, StringComparison.Ordinal))
  1614. {
  1615. text = string.Empty;
  1616. }
  1617. else if (!this.removeBaseAddress &&
  1618. text.Contains(ServiceMetadataExtension.BaseAddressPattern))
  1619. {
  1620. text = text.Replace(ServiceMetadataExtension.BaseAddressPattern, this.newBaseAddress);
  1621. }
  1622. else if (Uri.TryCreate(text, UriKind.Absolute, out uri))
  1623. {
  1624. Uri newUri = UpdateUri(uri);
  1625. if (newUri != null)
  1626. {
  1627. text = newUri.ToString();
  1628. }
  1629. }
  1630. base.WriteString(text);
  1631. }
  1632. public void UpdateUri(ref Uri uri, bool updateBaseAddressOnly = false)
  1633. {
  1634. Uri newUri = UpdateUri(uri, updateBaseAddressOnly);
  1635. if (newUri != null)
  1636. {
  1637. uri = newUri;
  1638. }
  1639. }
  1640. Uri UpdateUri(Uri uri, bool updateBaseAddressOnly = false)
  1641. {
  1642. // Ordinal comparison okay: we're filtering for auto-generated URIs which will
  1643. // always be based off the listenURI, so always match in case
  1644. if (uri.Host != oldHostName)
  1645. {
  1646. return null;
  1647. }
  1648. UriBuilder result = new UriBuilder(uri);
  1649. result.Host = this.newHostName;
  1650. if (!updateBaseAddressOnly)
  1651. {
  1652. int port;
  1653. if (uri.Scheme == this.requestScheme)
  1654. {
  1655. port = requestPort;
  1656. }
  1657. else if (!this.updatePortsByScheme.TryGetValue(uri.Scheme, out port))
  1658. {
  1659. return null;
  1660. }
  1661. result.Port = port;
  1662. }
  1663. return result.Uri;
  1664. }
  1665. }
  1666. }
  1667. }