1 //------------------------------------------------------------ 2 // Copyright (c) Microsoft Corporation. All rights reserved. 3 //------------------------------------------------------------ 4 #pragma warning disable 1634, 1691 5 namespace System.ServiceModel.Description 6 { 7 using System; 8 using System.Collections.Generic; 9 using System.Collections.ObjectModel; 10 using System.ComponentModel; 11 using System.IO; 12 using System.Linq; 13 using System.ServiceModel; 14 using System.ServiceModel.Administration; 15 using System.ServiceModel.Channels; 16 using System.ServiceModel.Dispatcher; 17 using System.ServiceModel.Web; 18 19 public class WebHttpBehavior : IEndpointBehavior, IWmiInstanceProvider 20 { 21 internal const string GET = "GET"; 22 internal const string POST = "POST"; 23 internal const string WildcardAction = "*"; 24 internal const string WildcardMethod = "*"; 25 internal static readonly string defaultStreamContentType = "application/octet-stream"; 26 internal static readonly string defaultCallbackParameterName = "callback"; 27 const string AddressPropertyName = "Address"; 28 WebMessageBodyStyle defaultBodyStyle; 29 WebMessageFormat defaultOutgoingReplyFormat; 30 WebMessageFormat defaultOutgoingRequestFormat; 31 XmlSerializerOperationBehavior.Reflector reflector; 32 UnwrappedTypesXmlSerializerManager xmlSerializerManager; 33 WebHttpBehavior()34 public WebHttpBehavior() 35 { 36 defaultOutgoingRequestFormat = WebMessageFormat.Xml; 37 defaultOutgoingReplyFormat = WebMessageFormat.Xml; 38 this.defaultBodyStyle = WebMessageBodyStyle.Bare; 39 xmlSerializerManager = new UnwrappedTypesXmlSerializerManager(); 40 } 41 Effect()42 internal delegate void Effect(); 43 44 public virtual WebMessageBodyStyle DefaultBodyStyle 45 { 46 get { return this.defaultBodyStyle; } 47 set 48 { 49 if (!WebMessageBodyStyleHelper.IsDefined(value)) 50 { 51 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value")); 52 } 53 this.defaultBodyStyle = value; 54 } 55 } 56 57 public virtual WebMessageFormat DefaultOutgoingRequestFormat 58 { 59 get 60 { 61 return this.defaultOutgoingRequestFormat; 62 } 63 set 64 { 65 if (!WebMessageFormatHelper.IsDefined(value)) 66 { 67 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value")); 68 } 69 this.defaultOutgoingRequestFormat = value; 70 } 71 } 72 73 public virtual WebMessageFormat DefaultOutgoingResponseFormat 74 { 75 get 76 { 77 return this.defaultOutgoingReplyFormat; 78 } 79 set 80 { 81 if (!WebMessageFormatHelper.IsDefined(value)) 82 { 83 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value")); 84 } 85 this.defaultOutgoingReplyFormat = value; 86 } 87 } 88 89 public virtual bool HelpEnabled { get; set; } 90 91 public virtual bool AutomaticFormatSelectionEnabled { get; set; } 92 93 public virtual bool FaultExceptionEnabled { get; set; } 94 95 internal Uri HelpUri { get; set; } 96 97 protected internal string JavascriptCallbackParameterName { get; set; } 98 AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)99 public virtual void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) 100 { 101 // do nothing 102 } 103 ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)104 public virtual void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) 105 { 106 if (endpoint == null) 107 { 108 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpoint"); 109 } 110 if (clientRuntime == null) 111 { 112 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("clientRuntime"); 113 } 114 WebMessageEncodingBindingElement webEncodingBindingElement = endpoint.Binding.CreateBindingElements().Find<WebMessageEncodingBindingElement>(); 115 if (webEncodingBindingElement != null && webEncodingBindingElement.CrossDomainScriptAccessEnabled) 116 { 117 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR2.CrossDomainJavascriptNotsupported)); 118 } 119 #pragma warning disable 56506 // Microsoft, endpoint.Contract is never null 120 this.reflector = new XmlSerializerOperationBehavior.Reflector(endpoint.Contract.Namespace, null); 121 foreach (OperationDescription od in endpoint.Contract.Operations) 122 #pragma warning restore 56506 123 { 124 #pragma warning disable 56506 // Microsoft, clientRuntime.Operations is never null 125 if (clientRuntime.Operations.Contains(od.Name)) 126 #pragma warning restore 56506 127 { 128 ClientOperation cop = clientRuntime.Operations[od.Name]; 129 IClientMessageFormatter requestClient = GetRequestClientFormatter(od, endpoint); 130 IClientMessageFormatter replyClient = GetReplyClientFormatter(od, endpoint); 131 cop.Formatter = new CompositeClientFormatter(requestClient, replyClient); 132 cop.SerializeRequest = true; 133 cop.DeserializeReply = od.Messages.Count > 1 && !IsUntypedMessage(od.Messages[1]); 134 } 135 } 136 AddClientErrorInspector(endpoint, clientRuntime); 137 } 138 ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)139 public virtual void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) 140 { 141 if (endpoint == null) 142 { 143 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpoint"); 144 } 145 if (endpointDispatcher == null) 146 { 147 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpointDispatcher"); 148 } 149 WebMessageEncodingBindingElement webEncodingBindingElement = endpoint.Binding.CreateBindingElements().Find<WebMessageEncodingBindingElement>(); 150 if (webEncodingBindingElement != null && webEncodingBindingElement.CrossDomainScriptAccessEnabled) 151 { 152 ISecurityCapabilities securityCapabilities = endpoint.Binding.GetProperty<ISecurityCapabilities>(new BindingParameterCollection()); 153 if (securityCapabilities.SupportsClientAuthentication) 154 { 155 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR2.CrossDomainJavascriptAuthNotSupported)); 156 } 157 if (endpoint.Contract.Behaviors.Contains(typeof(JavascriptCallbackBehaviorAttribute))) 158 { 159 JavascriptCallbackBehaviorAttribute behavior = endpoint.Contract.Behaviors[typeof(JavascriptCallbackBehaviorAttribute)] as JavascriptCallbackBehaviorAttribute; 160 this.JavascriptCallbackParameterName = behavior.UrlParameterName; 161 } 162 else 163 { 164 this.JavascriptCallbackParameterName = defaultCallbackParameterName; 165 } 166 endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new JavascriptCallbackMessageInspector(this.JavascriptCallbackParameterName)); 167 } 168 if (this.HelpEnabled) 169 { 170 this.HelpUri = new UriTemplate(HelpPage.OperationListHelpPageUriTemplate).BindByPosition(endpoint.ListenUri); 171 } 172 #pragma warning disable 56506 // Microsoft, endpoint.Contract is never null 173 this.reflector = new XmlSerializerOperationBehavior.Reflector(endpoint.Contract.Namespace, null); 174 #pragma warning restore 56506 175 176 // endpoint filter 177 endpointDispatcher.AddressFilter = new PrefixEndpointAddressMessageFilter(endpoint.Address); 178 endpointDispatcher.ContractFilter = new MatchAllMessageFilter(); 179 // operation selector 180 #pragma warning disable 56506 // Microsoft, endpointDispatcher.DispatchRuntime is never null 181 endpointDispatcher.DispatchRuntime.OperationSelector = this.GetOperationSelector(endpoint); 182 #pragma warning restore 56506 183 // unhandled operation 184 string actionStarOperationName = null; 185 #pragma warning disable 56506 // Microsoft, endpoint.Contract is never null 186 foreach (OperationDescription od in endpoint.Contract.Operations) 187 #pragma warning restore 56506 188 { 189 if (od.Messages[0].Direction == MessageDirection.Input 190 && od.Messages[0].Action == WildcardAction) 191 { 192 actionStarOperationName = od.Name; 193 break; 194 } 195 } 196 if (actionStarOperationName != null) 197 { 198 // WCF v1 installs any Action="*" op into UnhandledDispatchOperation, but WebHttpBehavior 199 // doesn't want this, so we 'move' that operation back into normal set of operations 200 #pragma warning disable 56506 // Microsoft, endpointDispatcher.DispatchRuntime.{Operations,UnhandledDispatchOperation} is never null 201 endpointDispatcher.DispatchRuntime.Operations.Add( 202 endpointDispatcher.DispatchRuntime.UnhandledDispatchOperation); 203 #pragma warning restore 56506 204 } 205 206 FormatSelectingMessageInspector formatSelectingMessageInspector = null; 207 string xmlContentType = null; 208 string jsonContentType = null; 209 210 211 if (webEncodingBindingElement != null) 212 { 213 XmlFormatMapping xmlFormatMapping = new XmlFormatMapping(webEncodingBindingElement.WriteEncoding, webEncodingBindingElement.ContentTypeMapper); 214 JsonFormatMapping jsonFormatMapping = new JsonFormatMapping(webEncodingBindingElement.WriteEncoding, webEncodingBindingElement.ContentTypeMapper); 215 216 xmlContentType = xmlFormatMapping.DefaultContentType.ToString(); 217 jsonContentType = jsonFormatMapping.DefaultContentType.ToString(); 218 219 if (AutomaticFormatSelectionEnabled) 220 { 221 formatSelectingMessageInspector = new FormatSelectingMessageInspector(this, new List<MultiplexingFormatMapping> { xmlFormatMapping, jsonFormatMapping }); 222 endpointDispatcher.DispatchRuntime.MessageInspectors.Add(formatSelectingMessageInspector); 223 } 224 } 225 else 226 { 227 xmlContentType = TextMessageEncoderFactory.GetContentType(XmlFormatMapping.defaultMediaType, TextEncoderDefaults.Encoding); 228 jsonContentType = JsonMessageEncoderFactory.GetContentType(null); 229 } 230 231 #pragma warning disable 56506 // Microsoft, endpointDispatcher.DispatchRuntime.UnhandledDispatchOperation is never null 232 // always install UnhandledDispatchOperation (WebHttpDispatchOperationSelector may choose not to use it) 233 endpointDispatcher.DispatchRuntime.UnhandledDispatchOperation = new DispatchOperation(endpointDispatcher.DispatchRuntime, "*", WildcardAction, WildcardAction); 234 endpointDispatcher.DispatchRuntime.UnhandledDispatchOperation.DeserializeRequest = false; 235 endpointDispatcher.DispatchRuntime.UnhandledDispatchOperation.SerializeReply = false; 236 endpointDispatcher.DispatchRuntime.UnhandledDispatchOperation.Invoker = new HttpUnhandledOperationInvoker { HelpUri = this.HelpUri }; 237 #pragma warning restore 56506 238 // install formatters and parameter inspectors 239 foreach (OperationDescription od in endpoint.Contract.Operations) 240 { 241 DispatchOperation dop = null; 242 #pragma warning disable 56506 // Microsoft, endpointDispatcher.DispatchRuntime, DispatchRuntime.Operations are never null 243 if (endpointDispatcher.DispatchRuntime.Operations.Contains(od.Name)) 244 #pragma warning restore 56506 245 { 246 dop = endpointDispatcher.DispatchRuntime.Operations[od.Name]; 247 } 248 #pragma warning disable 56506 // Microsoft, endpointDispatcher.DispatchRuntime.UnhandledDispatchOperation is never null 249 else if (endpointDispatcher.DispatchRuntime.UnhandledDispatchOperation.Name == od.Name) 250 { 251 dop = endpointDispatcher.DispatchRuntime.UnhandledDispatchOperation; 252 } 253 #pragma warning restore 56506 254 if (dop != null) 255 { 256 IDispatchMessageFormatter requestDispatch = GetRequestDispatchFormatter(od, endpoint); 257 IDispatchMessageFormatter replyDispatch = GetReplyDispatchFormatter(od, endpoint); 258 259 MultiplexingDispatchMessageFormatter replyDispatchAsMultiplexing = replyDispatch as MultiplexingDispatchMessageFormatter; 260 261 if (replyDispatchAsMultiplexing != null) 262 { 263 // here we are adding all default content types, despite the fact that 264 // some of the formatters in MultiplexingDispatchMessageFormatter might not be present 265 // i.e. the JSON formatter 266 267 replyDispatchAsMultiplexing.DefaultContentTypes.Add(WebMessageFormat.Xml, xmlContentType); 268 replyDispatchAsMultiplexing.DefaultContentTypes.Add(WebMessageFormat.Json, jsonContentType); 269 270 if (formatSelectingMessageInspector != null) 271 { 272 formatSelectingMessageInspector.RegisterOperation(od.Name, replyDispatchAsMultiplexing); 273 } 274 } 275 276 dop.Formatter = new CompositeDispatchFormatter(requestDispatch, replyDispatch); 277 dop.FaultFormatter = new WebFaultFormatter(dop.FaultFormatter); 278 dop.DeserializeRequest = (requestDispatch != null); 279 dop.SerializeReply = od.Messages.Count > 1 && (replyDispatch != null); 280 } 281 } 282 283 if (this.HelpEnabled) 284 { 285 HelpPage helpPage = new HelpPage(this, endpoint.Contract); 286 DispatchOperation dispatchOperation = new DispatchOperation(endpointDispatcher.DispatchRuntime, HelpOperationInvoker.OperationName, null, null) 287 { 288 DeserializeRequest = false, 289 SerializeReply = false, 290 Invoker = new HelpOperationInvoker(helpPage, endpointDispatcher.DispatchRuntime.UnhandledDispatchOperation.Invoker), 291 }; 292 endpointDispatcher.DispatchRuntime.Operations.Add(dispatchOperation); 293 } 294 AddServerErrorHandlers(endpoint, endpointDispatcher); 295 } 296 GetWmiProperties()297 internal virtual Dictionary<string, string> GetWmiProperties() 298 { 299 Dictionary<string, string> result = new Dictionary<string, string>(); 300 result.Add("DefaultBodyStyle", this.DefaultBodyStyle.ToString()); 301 result.Add("DefaultOutgoingRequestFormat", this.DefaultOutgoingRequestFormat.ToString()); 302 result.Add("DefaultOutgoingResponseFormat", this.DefaultOutgoingResponseFormat.ToString()); 303 return result; 304 } 305 GetWmiTypeName()306 internal virtual string GetWmiTypeName() 307 { 308 return "WebHttpBehavior"; 309 } 310 IWmiInstanceProvider.FillInstance(IWmiInstance wmiInstance)311 void IWmiInstanceProvider.FillInstance(IWmiInstance wmiInstance) 312 { 313 if (wmiInstance == null) 314 { 315 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("wmiInstance"); 316 } 317 Dictionary<string, string> properties = this.GetWmiProperties(); 318 foreach (string key in properties.Keys) 319 { 320 wmiInstance.SetProperty(key, properties[key]); 321 } 322 } 323 IWmiInstanceProvider.GetInstanceType()324 string IWmiInstanceProvider.GetInstanceType() 325 { 326 return GetWmiTypeName(); 327 } 328 Validate(ServiceEndpoint endpoint)329 public virtual void Validate(ServiceEndpoint endpoint) 330 { 331 if (endpoint == null) 332 { 333 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpoint"); 334 } 335 ValidateNoMessageHeadersPresent(endpoint); 336 ValidateBinding(endpoint); 337 ValidateContract(endpoint); 338 } 339 ValidateNoMessageHeadersPresent(ServiceEndpoint endpoint)340 void ValidateNoMessageHeadersPresent(ServiceEndpoint endpoint) 341 { 342 if (endpoint == null || endpoint.Address == null) 343 { 344 return; 345 } 346 EndpointAddress address = endpoint.Address; 347 if (address.Headers.Count > 0) 348 { 349 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(SR2.WebHttpServiceEndpointCannotHaveMessageHeaders, address))); 350 } 351 } 352 ValidateBinding(ServiceEndpoint endpoint)353 protected virtual void ValidateBinding(ServiceEndpoint endpoint) 354 { 355 ValidateIsWebHttpBinding(endpoint, this.GetType().ToString()); 356 } 357 GetWebMethod(OperationDescription od)358 internal static string GetWebMethod(OperationDescription od) 359 { 360 WebGetAttribute wga = od.Behaviors.Find<WebGetAttribute>(); 361 WebInvokeAttribute wia = od.Behaviors.Find<WebInvokeAttribute>(); 362 EnsureOk(wga, wia, od); 363 if (wga != null) 364 { 365 return GET; 366 } 367 else if (wia != null) 368 { 369 return wia.Method ?? POST; 370 } 371 else 372 { 373 return POST; 374 } 375 } 376 GetWebUriTemplate(OperationDescription od)377 internal static string GetWebUriTemplate(OperationDescription od) 378 { 379 // return exactly what is on the attribute 380 WebGetAttribute wga = od.Behaviors.Find<WebGetAttribute>(); 381 WebInvokeAttribute wia = od.Behaviors.Find<WebInvokeAttribute>(); 382 EnsureOk(wga, wia, od); 383 if (wga != null) 384 { 385 return wga.UriTemplate; 386 } 387 else if (wia != null) 388 { 389 return wia.UriTemplate; 390 } 391 else 392 { 393 return null; 394 } 395 } 396 GetDescription(OperationDescription od)397 internal static string GetDescription(OperationDescription od) 398 { 399 object[] attributes = null; 400 if (od.SyncMethod != null) 401 { 402 attributes = od.SyncMethod.GetCustomAttributes(typeof(DescriptionAttribute), true); 403 } 404 else if (od.BeginMethod != null) 405 { 406 attributes = od.BeginMethod.GetCustomAttributes(typeof(DescriptionAttribute), true); 407 } 408 else if (od.TaskMethod != null) 409 { 410 attributes = od.TaskMethod.GetCustomAttributes(typeof(DescriptionAttribute), true); 411 } 412 413 if (attributes != null && attributes.Length > 0) 414 { 415 return ((DescriptionAttribute)attributes[0]).Description; 416 } 417 else 418 { 419 return String.Empty; 420 } 421 } 422 IsTypedMessage(MessageDescription message)423 internal static bool IsTypedMessage(MessageDescription message) 424 { 425 return (message != null && message.MessageType != null); 426 } 427 IsUntypedMessage(MessageDescription message)428 internal static bool IsUntypedMessage(MessageDescription message) 429 { 430 if (message == null) 431 { 432 return false; 433 } 434 return (message.Body.ReturnValue != null && message.Body.Parts.Count == 0 && message.Body.ReturnValue.Type == typeof(Message)) || 435 (message.Body.ReturnValue == null && message.Body.Parts.Count == 1 && message.Body.Parts[0].Type == typeof(Message)); 436 } 437 MakeDummyMessageDescription(MessageDirection direction)438 internal static MessageDescription MakeDummyMessageDescription(MessageDirection direction) 439 { 440 MessageDescription messageDescription = new MessageDescription("urn:dummyAction", direction); 441 return messageDescription; 442 } 443 SupportsJsonFormat(OperationDescription od)444 internal static bool SupportsJsonFormat(OperationDescription od) 445 { 446 // if the type is XmlSerializable, then we cannot create a json serializer for it 447 DataContractSerializerOperationBehavior dcsob = od.Behaviors.Find<DataContractSerializerOperationBehavior>(); 448 return (dcsob != null); 449 } 450 ValidateIsWebHttpBinding(ServiceEndpoint serviceEndpoint, string behaviorName)451 internal static void ValidateIsWebHttpBinding(ServiceEndpoint serviceEndpoint, string behaviorName) 452 { 453 Binding binding = serviceEndpoint.Binding; 454 if (binding.Scheme != "http" && binding.Scheme != "https") 455 { 456 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( 457 SR2.GetString(SR2.WCFBindingCannotBeUsedWithUriOperationSelectorBehaviorBadScheme, 458 serviceEndpoint.Contract.Name, behaviorName))); 459 } 460 if (binding.MessageVersion != MessageVersion.None) 461 { 462 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( 463 SR2.GetString(SR2.WCFBindingCannotBeUsedWithUriOperationSelectorBehaviorBadMessageVersion, 464 serviceEndpoint.Address.Uri.AbsoluteUri, behaviorName))); 465 } 466 TransportBindingElement transportBindingElement = binding.CreateBindingElements().Find<TransportBindingElement>(); 467 if (transportBindingElement != null && !transportBindingElement.ManualAddressing) 468 { 469 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( 470 SR2.GetString(SR2.ManualAddressingCannotBeFalseWithTransportBindingElement, 471 serviceEndpoint.Address.Uri.AbsoluteUri, behaviorName, transportBindingElement.GetType().Name))); 472 } 473 } 474 GetBodyStyle(OperationDescription od)475 internal WebMessageBodyStyle GetBodyStyle(OperationDescription od) 476 { 477 WebGetAttribute wga = od.Behaviors.Find<WebGetAttribute>(); 478 WebInvokeAttribute wia = od.Behaviors.Find<WebInvokeAttribute>(); 479 EnsureOk(wga, wia, od); 480 if (wga != null) 481 { 482 return wga.GetBodyStyleOrDefault(this.DefaultBodyStyle); 483 } 484 else if (wia != null) 485 { 486 return wia.GetBodyStyleOrDefault(this.DefaultBodyStyle); 487 } 488 else 489 { 490 return this.DefaultBodyStyle; 491 } 492 } 493 GetDefaultClientFormatter(OperationDescription od, bool useJson, bool isWrapped)494 internal IClientMessageFormatter GetDefaultClientFormatter(OperationDescription od, bool useJson, bool isWrapped) 495 { 496 DataContractSerializerOperationBehavior dcsob = od.Behaviors.Find<DataContractSerializerOperationBehavior>(); 497 if (useJson) 498 { 499 if (dcsob == null) 500 { 501 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(SR2.JsonFormatRequiresDataContract, od.Name, od.DeclaringContract.Name, od.DeclaringContract.Namespace))); 502 } 503 return CreateDataContractJsonSerializerOperationFormatter(od, dcsob, isWrapped); 504 } 505 else 506 { 507 ClientRuntime clientRuntime = new ClientRuntime("name", ""); 508 ClientOperation cop = new ClientOperation(clientRuntime, "dummyClient", "urn:dummy"); 509 cop.Formatter = null; 510 511 if (dcsob != null) 512 { 513 (dcsob as IOperationBehavior).ApplyClientBehavior(od, cop); 514 return cop.Formatter; 515 } 516 XmlSerializerOperationBehavior xsob = od.Behaviors.Find<XmlSerializerOperationBehavior>(); 517 if (xsob != null) 518 { 519 xsob = new XmlSerializerOperationBehavior(od, xsob.XmlSerializerFormatAttribute, this.reflector); 520 (xsob as IOperationBehavior).ApplyClientBehavior(od, cop); 521 return cop.Formatter; 522 } 523 } 524 return null; 525 } 526 AddClientErrorInspector(ServiceEndpoint endpoint, ClientRuntime clientRuntime)527 protected virtual void AddClientErrorInspector(ServiceEndpoint endpoint, ClientRuntime clientRuntime) 528 { 529 if (!this.FaultExceptionEnabled) 530 { 531 clientRuntime.MessageInspectors.Add(new WebFaultClientMessageInspector()); 532 } 533 else 534 { 535 clientRuntime.MessageVersionNoneFaultsEnabled = true; 536 } 537 } 538 AddServerErrorHandlers(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)539 protected virtual void AddServerErrorHandlers(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) 540 { 541 if (!this.FaultExceptionEnabled) 542 { 543 WebErrorHandler errorHandler = new WebErrorHandler(this, endpoint.Contract, endpointDispatcher.DispatchRuntime.ChannelDispatcher.IncludeExceptionDetailInFaults); 544 endpointDispatcher.DispatchRuntime.ChannelDispatcher.ErrorHandlers.Add(errorHandler); 545 } 546 } 547 GetOperationSelector(ServiceEndpoint endpoint)548 protected virtual WebHttpDispatchOperationSelector GetOperationSelector(ServiceEndpoint endpoint) 549 { 550 return new WebHttpDispatchOperationSelector(endpoint); 551 } 552 GetQueryStringConverter(OperationDescription operationDescription)553 protected virtual QueryStringConverter GetQueryStringConverter(OperationDescription operationDescription) 554 { 555 return new QueryStringConverter(); 556 } 557 GetReplyClientFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)558 protected virtual IClientMessageFormatter GetReplyClientFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint) 559 { 560 if (operationDescription.Messages.Count < 2) 561 { 562 return null; 563 } 564 ValidateBodyParameters(operationDescription, false); 565 Type type; 566 if (TryGetStreamParameterType(operationDescription.Messages[1], operationDescription, false, out type)) 567 { 568 return new HttpStreamFormatter(operationDescription); 569 } 570 if (IsUntypedMessage(operationDescription.Messages[1])) 571 { 572 return new MessagePassthroughFormatter(); 573 } 574 WebMessageBodyStyle style = GetBodyStyle(operationDescription); 575 Type parameterType; 576 if (UseBareReplyFormatter(style, operationDescription, GetResponseFormat(operationDescription), out parameterType)) 577 { 578 return SingleBodyParameterMessageFormatter.CreateXmlAndJsonClientFormatter(operationDescription, parameterType, false, this.xmlSerializerManager); 579 } 580 else 581 { 582 MessageDescription temp = operationDescription.Messages[0]; 583 operationDescription.Messages[0] = MakeDummyMessageDescription(MessageDirection.Input); 584 IClientMessageFormatter result; 585 result = GetDefaultXmlAndJsonClientFormatter(operationDescription, !IsBareResponse(style)); 586 operationDescription.Messages[0] = temp; 587 return result; 588 } 589 } 590 UseBareReplyFormatter(WebMessageBodyStyle style, OperationDescription operationDescription, WebMessageFormat responseFormat, out Type parameterType)591 internal virtual bool UseBareReplyFormatter(WebMessageBodyStyle style, OperationDescription operationDescription, WebMessageFormat responseFormat, out Type parameterType) 592 { 593 parameterType = null; 594 return IsBareResponse(style) && TryGetNonMessageParameterType(operationDescription.Messages[1], operationDescription, false, out parameterType); 595 } 596 GetReplyDispatchFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)597 protected virtual IDispatchMessageFormatter GetReplyDispatchFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint) 598 { 599 if (operationDescription.Messages.Count < 2) 600 { 601 return null; 602 } 603 ValidateBodyParameters(operationDescription, false); 604 WebMessageFormat responseFormat = GetResponseFormat(operationDescription); 605 606 // Determine if we should add a json formatter; If the ResponseFormat is json, we always add the json formatter even if the 607 // operation is XmlSerializerFormat because the formatter constructor throws the exception: "json not valid with XmlSerializerFormat" [Microsoft] 608 bool useJson = (responseFormat == WebMessageFormat.Json || SupportsJsonFormat(operationDescription)); 609 610 IDispatchMessageFormatter innerFormatter; 611 Type type; 612 613 if (TryGetStreamParameterType(operationDescription.Messages[1], operationDescription, false, out type)) 614 { 615 innerFormatter = new ContentTypeSettingDispatchMessageFormatter(defaultStreamContentType, new HttpStreamFormatter(operationDescription)); 616 } 617 else if (IsUntypedMessage(operationDescription.Messages[1])) 618 { 619 innerFormatter = new MessagePassthroughFormatter(); 620 } 621 else 622 { 623 Type parameterType; 624 WebMessageBodyStyle style = GetBodyStyle(operationDescription); 625 Dictionary<WebMessageFormat, IDispatchMessageFormatter> formatters = new Dictionary<WebMessageFormat, IDispatchMessageFormatter>(); 626 627 if (UseBareReplyFormatter(style, operationDescription, responseFormat, out parameterType)) 628 { 629 formatters.Add(WebMessageFormat.Xml, SingleBodyParameterMessageFormatter.CreateDispatchFormatter(operationDescription, parameterType, false, false, this.xmlSerializerManager, null)); 630 if (useJson) 631 { 632 formatters.Add(WebMessageFormat.Json, SingleBodyParameterMessageFormatter.CreateDispatchFormatter(operationDescription, parameterType, false, true, this.xmlSerializerManager, this.JavascriptCallbackParameterName)); 633 } 634 } 635 else 636 { 637 MessageDescription temp = operationDescription.Messages[0]; 638 operationDescription.Messages[0] = MakeDummyMessageDescription(MessageDirection.Input); 639 formatters.Add(WebMessageFormat.Xml, GetDefaultDispatchFormatter(operationDescription, false, !IsBareResponse(style))); 640 if (useJson) 641 { 642 formatters.Add(WebMessageFormat.Json, GetDefaultDispatchFormatter(operationDescription, true, !IsBareResponse(style))); 643 } 644 operationDescription.Messages[0] = temp; 645 } 646 innerFormatter = new MultiplexingDispatchMessageFormatter(formatters, responseFormat); 647 } 648 649 return innerFormatter; 650 } 651 GetRequestClientFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)652 protected virtual IClientMessageFormatter GetRequestClientFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint) 653 { 654 WebMessageFormat requestFormat = GetRequestFormat(operationDescription); 655 bool useJson = (requestFormat == WebMessageFormat.Json); 656 WebMessageEncodingBindingElement webEncoding = (useJson) ? endpoint.Binding.CreateBindingElements().Find<WebMessageEncodingBindingElement>() : null; 657 IClientMessageFormatter innerFormatter = null; 658 659 // get some validation errors by creating "throwAway" formatter 660 661 // validate that endpoint.Address is not null before accessing the endpoint.Address.Uri. This is to avoid throwing a NullRefException while constructing a UriTemplateClientFormatter 662 if (endpoint.Address == null) 663 { 664 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( 665 SR2.GetString(SR2.ServiceEndpointMustHaveNonNullAddress, typeof(ServiceEndpoint), typeof(ChannelFactory), typeof(WebHttpEndpoint), AddressPropertyName, typeof(ServiceEndpoint)))); 666 } 667 668 UriTemplateClientFormatter throwAway = new UriTemplateClientFormatter(operationDescription, null, GetQueryStringConverter(operationDescription), endpoint.Address.Uri, false, endpoint.Contract.Name); 669 int numUriVariables = throwAway.pathMapping.Count + throwAway.queryMapping.Count; 670 bool isStream = false; 671 HideReplyMessage(operationDescription, delegate() 672 { 673 WebMessageBodyStyle style = GetBodyStyle(operationDescription); 674 bool isUntypedWhenUriParamsNotConsidered = false; 675 Effect doBodyFormatter = delegate() 676 { 677 if (numUriVariables != 0) 678 { 679 EnsureNotUntypedMessageNorMessageContract(operationDescription); 680 } 681 // get body formatter 682 ValidateBodyParameters(operationDescription, true); 683 IClientMessageFormatter baseFormatter; 684 Type parameterType; 685 if (TryGetStreamParameterType(operationDescription.Messages[0], operationDescription, true, out parameterType)) 686 { 687 isStream = true; 688 baseFormatter = new HttpStreamFormatter(operationDescription); 689 } 690 else if (UseBareRequestFormatter(style, operationDescription, out parameterType)) 691 { 692 baseFormatter = SingleBodyParameterMessageFormatter.CreateClientFormatter(operationDescription, parameterType, true, useJson, this.xmlSerializerManager); 693 } 694 else 695 { 696 baseFormatter = GetDefaultClientFormatter(operationDescription, useJson, !IsBareRequest(style)); 697 } 698 innerFormatter = baseFormatter; 699 isUntypedWhenUriParamsNotConsidered = IsUntypedMessage(operationDescription.Messages[0]); 700 }; 701 if (numUriVariables == 0) 702 { 703 if (IsUntypedMessage(operationDescription.Messages[0])) 704 { 705 ValidateBodyParameters(operationDescription, true); 706 innerFormatter = new MessagePassthroughFormatter(); 707 isUntypedWhenUriParamsNotConsidered = true; 708 } 709 else if (IsTypedMessage(operationDescription.Messages[0])) 710 { 711 ValidateBodyParameters(operationDescription, true); 712 innerFormatter = GetDefaultClientFormatter(operationDescription, useJson, !IsBareRequest(style)); 713 } 714 else 715 { 716 doBodyFormatter(); 717 } 718 } 719 else 720 { 721 HideRequestUriTemplateParameters(operationDescription, throwAway, delegate() 722 { 723 CloneMessageDescriptionsBeforeActing(operationDescription, delegate() 724 { 725 doBodyFormatter(); 726 }); 727 }); 728 } 729 innerFormatter = new UriTemplateClientFormatter(operationDescription, innerFormatter, GetQueryStringConverter(operationDescription), endpoint.Address.Uri, isUntypedWhenUriParamsNotConsidered, endpoint.Contract.Name); 730 }); 731 string defaultContentType = GetDefaultContentType(isStream, useJson, webEncoding); 732 if (!string.IsNullOrEmpty(defaultContentType)) 733 { 734 innerFormatter = new ContentTypeSettingClientMessageFormatter(defaultContentType, innerFormatter); 735 } 736 return innerFormatter; 737 } 738 GetRequestDispatchFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)739 protected virtual IDispatchMessageFormatter GetRequestDispatchFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint) 740 { 741 IDispatchMessageFormatter result = null; 742 // get some validation errors by creating "throwAway" formatter 743 UriTemplateDispatchFormatter throwAway = new UriTemplateDispatchFormatter(operationDescription, null, GetQueryStringConverter(operationDescription), endpoint.Contract.Name, endpoint.Address.Uri); 744 int numUriVariables = throwAway.pathMapping.Count + throwAway.queryMapping.Count; 745 HideReplyMessage(operationDescription, delegate() 746 { 747 WebMessageBodyStyle style = GetBodyStyle(operationDescription); 748 Effect doBodyFormatter = delegate() 749 { 750 if (numUriVariables != 0) 751 { 752 EnsureNotUntypedMessageNorMessageContract(operationDescription); 753 } 754 // get body formatter 755 ValidateBodyParameters(operationDescription, true); 756 Type type; 757 if (TryGetStreamParameterType(operationDescription.Messages[0], operationDescription, true, out type)) 758 { 759 result = new HttpStreamFormatter(operationDescription); 760 } 761 else 762 { 763 Type parameterType; 764 if (UseBareRequestFormatter(style, operationDescription, out parameterType)) 765 { 766 result = SingleBodyParameterMessageFormatter.CreateXmlAndJsonDispatchFormatter(operationDescription, parameterType, true, this.xmlSerializerManager, this.JavascriptCallbackParameterName); 767 } 768 else 769 { 770 result = GetDefaultXmlAndJsonDispatchFormatter(operationDescription, !IsBareRequest(style)); 771 } 772 } 773 }; 774 if (numUriVariables == 0) 775 { 776 if (IsUntypedMessage(operationDescription.Messages[0])) 777 { 778 ValidateBodyParameters(operationDescription, true); 779 result = new MessagePassthroughFormatter(); 780 } 781 else if (IsTypedMessage(operationDescription.Messages[0])) 782 { 783 ValidateBodyParameters(operationDescription, true); 784 result = GetDefaultXmlAndJsonDispatchFormatter(operationDescription, !IsBareRequest(style)); 785 } 786 else 787 { 788 doBodyFormatter(); 789 } 790 } 791 else 792 { 793 HideRequestUriTemplateParameters(operationDescription, throwAway, delegate() 794 { 795 CloneMessageDescriptionsBeforeActing(operationDescription, delegate() 796 { 797 doBodyFormatter(); 798 }); 799 }); 800 } 801 result = new UriTemplateDispatchFormatter(operationDescription, result, GetQueryStringConverter(operationDescription), endpoint.Contract.Name, endpoint.Address.Uri); 802 }); 803 return result; 804 } 805 CloneMessageDescriptionsBeforeActing(OperationDescription operationDescription, Effect effect)806 static void CloneMessageDescriptionsBeforeActing(OperationDescription operationDescription, Effect effect) 807 { 808 MessageDescription originalRequest = operationDescription.Messages[0]; 809 bool thereIsAReply = operationDescription.Messages.Count > 1; 810 MessageDescription originalReply = thereIsAReply ? operationDescription.Messages[1] : null; 811 operationDescription.Messages[0] = originalRequest.Clone(); 812 if (thereIsAReply) 813 { 814 operationDescription.Messages[1] = originalReply.Clone(); 815 } 816 effect(); 817 operationDescription.Messages[0] = originalRequest; 818 if (thereIsAReply) 819 { 820 operationDescription.Messages[1] = originalReply; 821 } 822 } 823 UseBareRequestFormatter(WebMessageBodyStyle style, OperationDescription operationDescription, out Type parameterType)824 internal virtual bool UseBareRequestFormatter(WebMessageBodyStyle style, OperationDescription operationDescription, out Type parameterType) 825 { 826 parameterType = null; 827 return IsBareRequest(style) && TryGetNonMessageParameterType(operationDescription.Messages[0], operationDescription, true, out parameterType); 828 } 829 CloneParts(MessageDescription md)830 static Collection<MessagePartDescription> CloneParts(MessageDescription md) 831 { 832 MessagePartDescriptionCollection bodyParameters = md.Body.Parts; 833 Collection<MessagePartDescription> bodyParametersClone = new Collection<MessagePartDescription>(); 834 for (int i = 0; i < bodyParameters.Count; ++i) 835 { 836 MessagePartDescription copy = bodyParameters[i].Clone(); 837 bodyParametersClone.Add(copy); 838 } 839 return bodyParametersClone; 840 } 841 EnsureNotUntypedMessageNorMessageContract(OperationDescription operationDescription)842 static void EnsureNotUntypedMessageNorMessageContract(OperationDescription operationDescription) 843 { 844 // Called when there are UriTemplate parameters. UT does not compose with Message 845 // or MessageContract because the SOAP and REST programming models must be uniform here. 846 bool isUnadornedWebGet = false; 847 if (GetWebMethod(operationDescription) == GET && GetWebUriTemplate(operationDescription) == null) 848 { 849 isUnadornedWebGet = true; 850 } 851 if (IsTypedMessage(operationDescription.Messages[0])) 852 { 853 if (isUnadornedWebGet) 854 { 855 // WebGet will give you UriTemplate parameters by default. 856 // We need a special error message for this case to prevent confusion. 857 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( 858 SR2.GetString(SR2.GETCannotHaveMCParameter, operationDescription.Name, operationDescription.DeclaringContract.Name, operationDescription.Messages[0].MessageType.Name))); 859 } 860 else 861 { 862 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString( 863 SR2.UTParamsDoNotComposeWithMessageContract, operationDescription.Name, operationDescription.DeclaringContract.Name))); 864 } 865 } 866 867 if (IsUntypedMessage(operationDescription.Messages[0])) 868 { 869 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString( 870 SR2.UTParamsDoNotComposeWithMessage, operationDescription.Name, operationDescription.DeclaringContract.Name))); 871 } 872 } 873 EnsureOk(WebGetAttribute wga, WebInvokeAttribute wia, OperationDescription od)874 static void EnsureOk(WebGetAttribute wga, WebInvokeAttribute wia, OperationDescription od) 875 { 876 if (wga != null && wia != null) 877 { 878 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( 879 SR2.GetString(SR2.MultipleWebAttributes, od.Name, od.DeclaringContract.Name))); 880 } 881 } 882 HideReplyMessage(OperationDescription operationDescription, Effect effect)883 static void HideReplyMessage(OperationDescription operationDescription, Effect effect) 884 { 885 MessageDescription temp = null; 886 if (operationDescription.Messages.Count > 1) 887 { 888 temp = operationDescription.Messages[1]; 889 operationDescription.Messages[1] = MakeDummyMessageDescription(MessageDirection.Output); 890 } 891 effect(); 892 if (operationDescription.Messages.Count > 1) 893 { 894 operationDescription.Messages[1] = temp; 895 } 896 } 897 HideRequestUriTemplateParameters(OperationDescription operationDescription, UriTemplateClientFormatter throwAway, Effect effect)898 static void HideRequestUriTemplateParameters(OperationDescription operationDescription, UriTemplateClientFormatter throwAway, Effect effect) 899 { 900 HideRequestUriTemplateParameters(operationDescription, throwAway.pathMapping, throwAway.queryMapping, effect); 901 } 902 HideRequestUriTemplateParameters(OperationDescription operationDescription, UriTemplateDispatchFormatter throwAway, Effect effect)903 internal static void HideRequestUriTemplateParameters(OperationDescription operationDescription, UriTemplateDispatchFormatter throwAway, Effect effect) 904 { 905 HideRequestUriTemplateParameters(operationDescription, throwAway.pathMapping, throwAway.queryMapping, effect); 906 } 907 HideRequestUriTemplateParameters(OperationDescription operationDescription, Dictionary<int, string> pathMapping, Dictionary<int, KeyValuePair<string, Type>> queryMapping, Effect effect)908 static void HideRequestUriTemplateParameters(OperationDescription operationDescription, Dictionary<int, string> pathMapping, Dictionary<int, KeyValuePair<string, Type>> queryMapping, Effect effect) 909 { 910 // mutate description to hide UriTemplate parameters 911 Collection<MessagePartDescription> originalParts = CloneParts(operationDescription.Messages[0]); 912 Collection<MessagePartDescription> parts = CloneParts(operationDescription.Messages[0]); 913 operationDescription.Messages[0].Body.Parts.Clear(); 914 int newIndex = 0; 915 for (int i = 0; i < parts.Count; ++i) 916 { 917 if (!pathMapping.ContainsKey(i) && !queryMapping.ContainsKey(i)) 918 { 919 operationDescription.Messages[0].Body.Parts.Add(parts[i]); 920 parts[i].Index = newIndex++; 921 } 922 } 923 effect(); 924 // unmutate description 925 operationDescription.Messages[0].Body.Parts.Clear(); 926 for (int i = 0; i < originalParts.Count; ++i) 927 { 928 operationDescription.Messages[0].Body.Parts.Add(originalParts[i]); 929 } 930 } 931 IsBareRequest(WebMessageBodyStyle style)932 static bool IsBareRequest(WebMessageBodyStyle style) 933 { 934 return (style == WebMessageBodyStyle.Bare || style == WebMessageBodyStyle.WrappedResponse); 935 } 936 IsBareResponse(WebMessageBodyStyle style)937 static bool IsBareResponse(WebMessageBodyStyle style) 938 { 939 return (style == WebMessageBodyStyle.Bare || style == WebMessageBodyStyle.WrappedRequest); 940 } 941 TryGetNonMessageParameterType(MessageDescription message, OperationDescription declaringOperation, bool isRequest, out Type type)942 internal static bool TryGetNonMessageParameterType(MessageDescription message, OperationDescription declaringOperation, bool isRequest, out Type type) 943 { 944 type = null; 945 if (message == null) 946 { 947 return true; 948 } 949 if (IsTypedMessage(message) || IsUntypedMessage(message)) 950 { 951 return false; 952 } 953 if (isRequest) 954 { 955 if (message.Body.Parts.Count > 1) 956 { 957 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(SR2.AtMostOneRequestBodyParameterAllowedForUnwrappedMessages, declaringOperation.Name, declaringOperation.DeclaringContract.Name))); 958 } 959 if (message.Body.Parts.Count == 1 && message.Body.Parts[0].Type != typeof(void)) 960 { 961 type = message.Body.Parts[0].Type; 962 } 963 return true; 964 } 965 else 966 { 967 if (message.Body.Parts.Count > 0) 968 { 969 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(SR2.OnlyReturnValueBodyParameterAllowedForUnwrappedMessages, declaringOperation.Name, declaringOperation.DeclaringContract.Name))); 970 } 971 if (message.Body.ReturnValue != null && message.Body.ReturnValue.Type != typeof(void)) 972 { 973 type = message.Body.ReturnValue.Type; 974 } 975 return true; 976 } 977 } 978 TryGetStreamParameterType(MessageDescription message, OperationDescription declaringOperation, bool isRequest, out Type type)979 static bool TryGetStreamParameterType(MessageDescription message, OperationDescription declaringOperation, bool isRequest, out Type type) 980 { 981 type = null; 982 if (message == null || IsTypedMessage(message) || IsUntypedMessage(message)) 983 { 984 return false; 985 } 986 if (isRequest) 987 { 988 bool hasStream = false; 989 for (int i = 0; i < message.Body.Parts.Count; ++i) 990 { 991 if (typeof(Stream) == message.Body.Parts[i].Type) 992 { 993 type = message.Body.Parts[i].Type; 994 hasStream = true; 995 break; 996 } 997 998 } 999 if (hasStream && message.Body.Parts.Count > 1) 1000 { 1001 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR2.GetString(SR2.AtMostOneRequestBodyParameterAllowedForStream, declaringOperation.Name, declaringOperation.DeclaringContract.Name))); 1002 } 1003 return hasStream; 1004 } 1005 else 1006 { 1007 // validate that the stream is not an out or ref param 1008 for (int i = 0; i < message.Body.Parts.Count; ++i) 1009 { 1010 if (typeof(Stream) == message.Body.Parts[i].Type) 1011 { 1012 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR2.GetString(SR2.NoOutOrRefStreamParametersAllowed, message.Body.Parts[i].Name, declaringOperation.Name, declaringOperation.DeclaringContract.Name))); 1013 } 1014 } 1015 if (message.Body.ReturnValue != null && typeof(Stream) == message.Body.ReturnValue.Type) 1016 { 1017 // validate that there are no out or ref params 1018 if (message.Body.Parts.Count > 0) 1019 { 1020 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR2.GetString(SR2.NoOutOrRefParametersAllowedWithStreamResult, declaringOperation.Name, declaringOperation.DeclaringContract.Name))); 1021 } 1022 type = message.Body.ReturnValue.Type; 1023 return true; 1024 } 1025 1026 else 1027 { 1028 return false; 1029 } 1030 } 1031 } 1032 ValidateAtMostOneStreamParameter(OperationDescription operation, bool request)1033 static void ValidateAtMostOneStreamParameter(OperationDescription operation, bool request) 1034 { 1035 Type dummy; 1036 if (request) 1037 { 1038 TryGetStreamParameterType(operation.Messages[0], operation, true, out dummy); 1039 } 1040 else 1041 { 1042 if (operation.Messages.Count > 1) 1043 { 1044 TryGetStreamParameterType(operation.Messages[1], operation, false, out dummy); 1045 } 1046 } 1047 } 1048 GetDefaultContentType(bool isStream, bool useJson, WebMessageEncodingBindingElement webEncoding)1049 string GetDefaultContentType(bool isStream, bool useJson, WebMessageEncodingBindingElement webEncoding) 1050 { 1051 if (isStream) 1052 { 1053 return defaultStreamContentType; 1054 } 1055 else if (useJson) 1056 { 1057 return JsonMessageEncoderFactory.GetContentType(webEncoding); 1058 } 1059 else 1060 { 1061 return null; 1062 } 1063 } 1064 GetDefaultDispatchFormatter(OperationDescription od, bool useJson, bool isWrapped)1065 IDispatchMessageFormatter GetDefaultDispatchFormatter(OperationDescription od, bool useJson, bool isWrapped) 1066 { 1067 DataContractSerializerOperationBehavior dcsob = od.Behaviors.Find<DataContractSerializerOperationBehavior>(); 1068 if (useJson) 1069 { 1070 if (dcsob == null) 1071 { 1072 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(SR2.JsonFormatRequiresDataContract, od.Name, od.DeclaringContract.Name, od.DeclaringContract.Namespace))); 1073 } 1074 return CreateDataContractJsonSerializerOperationFormatter(od, dcsob, isWrapped); 1075 } 1076 else 1077 { 1078 EndpointDispatcher dummyED = new EndpointDispatcher(new EndpointAddress("http://localhost/"), "name", ""); 1079 DispatchRuntime dispatchRuntime = dummyED.DispatchRuntime; 1080 DispatchOperation dop = new DispatchOperation(dispatchRuntime, "dummyDispatch", "urn:dummy"); 1081 dop.Formatter = null; 1082 1083 if (dcsob != null) 1084 { 1085 (dcsob as IOperationBehavior).ApplyDispatchBehavior(od, dop); 1086 return dop.Formatter; 1087 } 1088 XmlSerializerOperationBehavior xsob = od.Behaviors.Find<XmlSerializerOperationBehavior>(); 1089 if (xsob != null) 1090 { 1091 xsob = new XmlSerializerOperationBehavior(od, xsob.XmlSerializerFormatAttribute, this.reflector); 1092 (xsob as IOperationBehavior).ApplyDispatchBehavior(od, dop); 1093 return dop.Formatter; 1094 } 1095 } 1096 return null; 1097 } 1098 CreateDataContractJsonSerializerOperationFormatter(OperationDescription od, DataContractSerializerOperationBehavior dcsob, bool isWrapped)1099 internal virtual DataContractJsonSerializerOperationFormatter CreateDataContractJsonSerializerOperationFormatter(OperationDescription od, DataContractSerializerOperationBehavior dcsob, bool isWrapped) 1100 { 1101 return new DataContractJsonSerializerOperationFormatter(od, dcsob.MaxItemsInObjectGraph, dcsob.IgnoreExtensionDataObject, dcsob.DataContractSurrogate, isWrapped, false, JavascriptCallbackParameterName); 1102 } 1103 GetDefaultXmlAndJsonClientFormatter(OperationDescription od, bool isWrapped)1104 IClientMessageFormatter GetDefaultXmlAndJsonClientFormatter(OperationDescription od, bool isWrapped) 1105 { 1106 IClientMessageFormatter xmlFormatter = GetDefaultClientFormatter(od, false, isWrapped); 1107 if (!SupportsJsonFormat(od)) 1108 { 1109 return xmlFormatter; 1110 } 1111 IClientMessageFormatter jsonFormatter = GetDefaultClientFormatter(od, true, isWrapped); 1112 Dictionary<WebContentFormat, IClientMessageFormatter> map = new Dictionary<WebContentFormat, IClientMessageFormatter>(); 1113 map.Add(WebContentFormat.Xml, xmlFormatter); 1114 map.Add(WebContentFormat.Json, jsonFormatter); 1115 // In case there is no format property, the default formatter to use is XML 1116 return new DemultiplexingClientMessageFormatter(map, xmlFormatter); 1117 } 1118 GetDefaultXmlAndJsonDispatchFormatter(OperationDescription od, bool isWrapped)1119 IDispatchMessageFormatter GetDefaultXmlAndJsonDispatchFormatter(OperationDescription od, bool isWrapped) 1120 { 1121 IDispatchMessageFormatter xmlFormatter = GetDefaultDispatchFormatter(od, false, isWrapped); 1122 if (!SupportsJsonFormat(od)) 1123 { 1124 return xmlFormatter; 1125 } 1126 IDispatchMessageFormatter jsonFormatter = GetDefaultDispatchFormatter(od, true, isWrapped); 1127 Dictionary<WebContentFormat, IDispatchMessageFormatter> map = new Dictionary<WebContentFormat, IDispatchMessageFormatter>(); 1128 map.Add(WebContentFormat.Xml, xmlFormatter); 1129 map.Add(WebContentFormat.Json, jsonFormatter); 1130 return new DemultiplexingDispatchMessageFormatter(map, xmlFormatter); 1131 } 1132 GetRequestFormat(OperationDescription od)1133 internal WebMessageFormat GetRequestFormat(OperationDescription od) 1134 { 1135 WebGetAttribute wga = od.Behaviors.Find<WebGetAttribute>(); 1136 WebInvokeAttribute wia = od.Behaviors.Find<WebInvokeAttribute>(); 1137 EnsureOk(wga, wia, od); 1138 if (wga != null) 1139 { 1140 return wga.IsRequestFormatSetExplicitly ? wga.RequestFormat : this.DefaultOutgoingRequestFormat; 1141 } 1142 else if (wia != null) 1143 { 1144 return wia.IsRequestFormatSetExplicitly ? wia.RequestFormat : this.DefaultOutgoingRequestFormat; 1145 } 1146 else 1147 { 1148 return this.DefaultOutgoingRequestFormat; 1149 } 1150 } 1151 GetResponseFormat(OperationDescription od)1152 internal WebMessageFormat GetResponseFormat(OperationDescription od) 1153 { 1154 WebGetAttribute wga = od.Behaviors.Find<WebGetAttribute>(); 1155 WebInvokeAttribute wia = od.Behaviors.Find<WebInvokeAttribute>(); 1156 EnsureOk(wga, wia, od); 1157 if (wga != null) 1158 { 1159 return wga.IsResponseFormatSetExplicitly ? wga.ResponseFormat : this.DefaultOutgoingResponseFormat; 1160 } 1161 else if (wia != null) 1162 { 1163 return wia.IsResponseFormatSetExplicitly ? wia.ResponseFormat : this.DefaultOutgoingResponseFormat; 1164 } 1165 else 1166 { 1167 return this.DefaultOutgoingResponseFormat; 1168 } 1169 } 1170 ValidateBodyParameters(OperationDescription operation, bool request)1171 void ValidateBodyParameters(OperationDescription operation, bool request) 1172 { 1173 string method = GetWebMethod(operation); 1174 if (request) 1175 { 1176 ValidateGETHasNoBody(operation, method); 1177 } 1178 // validate that if bare is chosen for request/response, then at most 1 parameter is possible 1179 ValidateBodyStyle(operation, request); 1180 // validate if the request or response body is a stream, no other body parameters 1181 // can be specified 1182 ValidateAtMostOneStreamParameter(operation, request); 1183 } 1184 ValidateBodyStyle(OperationDescription operation, bool request)1185 void ValidateBodyStyle(OperationDescription operation, bool request) 1186 { 1187 WebMessageBodyStyle style = GetBodyStyle(operation); 1188 Type dummy; 1189 if (request && IsBareRequest(style)) 1190 { 1191 TryGetNonMessageParameterType(operation.Messages[0], operation, true, out dummy); 1192 } 1193 if (!request && operation.Messages.Count > 1 && IsBareResponse(style)) 1194 { 1195 TryGetNonMessageParameterType(operation.Messages[1], operation, false, out dummy); 1196 } 1197 } 1198 ValidateGETHasNoBody(OperationDescription operation, string method)1199 void ValidateGETHasNoBody(OperationDescription operation, string method) 1200 { 1201 if (method == GET) 1202 { 1203 if (!IsUntypedMessage(operation.Messages[0]) && operation.Messages[0].Body.Parts.Count != 0) 1204 { 1205 if (!IsTypedMessage(operation.Messages[0])) 1206 { 1207 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( 1208 SR2.GetString(SR2.GETCannotHaveBody, operation.Name, operation.DeclaringContract.Name, operation.Messages[0].Body.Parts[0].Name))); 1209 } 1210 else 1211 { 1212 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( 1213 SR2.GetString(SR2.GETCannotHaveMCParameter, operation.Name, operation.DeclaringContract.Name, operation.Messages[0].MessageType.Name))); 1214 } 1215 } 1216 } 1217 } 1218 ValidateContract(ServiceEndpoint endpoint)1219 void ValidateContract(ServiceEndpoint endpoint) 1220 { 1221 foreach (OperationDescription od in endpoint.Contract.Operations) 1222 { 1223 ValidateNoOperationHasEncodedXmlSerializer(od); 1224 ValidateNoMessageContractHeaders(od.Messages[0], od.Name, endpoint.Contract.Name); 1225 ValidateNoBareMessageContractWithMultipleParts(od.Messages[0], od.Name, endpoint.Contract.Name); 1226 ValidateNoMessageContractWithStream(od.Messages[0], od.Name, endpoint.Contract.Name); 1227 if (od.Messages.Count > 1) 1228 { 1229 ValidateNoMessageContractHeaders(od.Messages[1], od.Name, endpoint.Contract.Name); 1230 ValidateNoBareMessageContractWithMultipleParts(od.Messages[1], od.Name, endpoint.Contract.Name); 1231 ValidateNoMessageContractWithStream(od.Messages[1], od.Name, endpoint.Contract.Name); 1232 } 1233 } 1234 } 1235 IsXmlSerializerFaultFormat(OperationDescription operationDescription)1236 internal static bool IsXmlSerializerFaultFormat(OperationDescription operationDescription) 1237 { 1238 XmlSerializerOperationBehavior xsob = operationDescription.Behaviors.Find<XmlSerializerOperationBehavior>(); 1239 return (xsob != null && xsob.XmlSerializerFormatAttribute.SupportFaults); 1240 } 1241 ValidateNoMessageContractWithStream(MessageDescription md, string opName, string contractName)1242 void ValidateNoMessageContractWithStream(MessageDescription md, string opName, string contractName) 1243 { 1244 if (IsTypedMessage(md)) 1245 { 1246 foreach (MessagePartDescription description in md.Body.Parts) 1247 { 1248 if (description.Type == typeof(Stream)) 1249 { 1250 throw System.ServiceModel.DiagnosticUtility.ExceptionUtility.ThrowHelperError( 1251 new InvalidOperationException(System.ServiceModel.SR2.GetString(System.ServiceModel.SR2.StreamBodyMemberNotSupported, this.GetType().ToString(), contractName, opName, md.MessageType.ToString(), description.Name))); 1252 } 1253 } 1254 } 1255 } 1256 ValidateNoOperationHasEncodedXmlSerializer(OperationDescription od)1257 void ValidateNoOperationHasEncodedXmlSerializer(OperationDescription od) 1258 { 1259 XmlSerializerOperationBehavior xsob = od.Behaviors.Find<XmlSerializerOperationBehavior>(); 1260 if (xsob != null && (xsob.XmlSerializerFormatAttribute.Style == OperationFormatStyle.Rpc || xsob.XmlSerializerFormatAttribute.IsEncoded)) 1261 { 1262 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(SR2.RpcEncodedNotSupportedForNoneMessageVersion, od.Name, od.DeclaringContract.Name, od.DeclaringContract.Namespace))); 1263 } 1264 } 1265 ValidateNoBareMessageContractWithMultipleParts(MessageDescription md, string opName, string contractName)1266 void ValidateNoBareMessageContractWithMultipleParts(MessageDescription md, string opName, string contractName) 1267 { 1268 if (IsTypedMessage(md) && md.Body.WrapperName == null) 1269 { 1270 if (md.Body.Parts.Count > 1) 1271 { 1272 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( 1273 SR2.GetString(SR2.InvalidMessageContractWithoutWrapperName, opName, contractName, md.MessageType))); 1274 } 1275 if (md.Body.Parts.Count == 1 && md.Body.Parts[0].Multiple) 1276 { 1277 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(SR2.MCAtMostOneRequestBodyParameterAllowedForUnwrappedMessages, opName, contractName, md.MessageType))); 1278 } 1279 } 1280 } 1281 ValidateNoMessageContractHeaders(MessageDescription md, string opName, string contractName)1282 void ValidateNoMessageContractHeaders(MessageDescription md, string opName, string contractName) 1283 { 1284 if (md.Headers.Count != 0) 1285 { 1286 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( 1287 SR2.GetString(SR2.InvalidMethodWithSOAPHeaders, opName, contractName))); 1288 } 1289 } 1290 1291 internal class MessagePassthroughFormatter : IClientMessageFormatter, IDispatchMessageFormatter 1292 { DeserializeReply(Message message, object[] parameters)1293 public object DeserializeReply(Message message, object[] parameters) 1294 { 1295 return message; 1296 } 1297 DeserializeRequest(Message message, object[] parameters)1298 public void DeserializeRequest(Message message, object[] parameters) 1299 { 1300 parameters[0] = message; 1301 } 1302 SerializeReply(MessageVersion messageVersion, object[] parameters, object result)1303 public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result) 1304 { 1305 return result as Message; 1306 } 1307 SerializeRequest(MessageVersion messageVersion, object[] parameters)1308 public Message SerializeRequest(MessageVersion messageVersion, object[] parameters) 1309 { 1310 return parameters[0] as Message; 1311 } 1312 } 1313 TrySetupJavascriptCallback(string callbackParameterName)1314 static internal JavascriptCallbackResponseMessageProperty TrySetupJavascriptCallback(string callbackParameterName) 1315 { 1316 JavascriptCallbackResponseMessageProperty javascriptProperty = null; 1317 if (!String.IsNullOrEmpty(callbackParameterName) && 1318 !OperationContext.Current.OutgoingMessageProperties.TryGetValue<JavascriptCallbackResponseMessageProperty>(JavascriptCallbackResponseMessageProperty.Name, out javascriptProperty)) 1319 { 1320 UriTemplateMatch match = WebOperationContext.Current.IncomingRequest.UriTemplateMatch; 1321 if (match != null && 1322 match.QueryParameters.AllKeys.Contains(callbackParameterName)) 1323 { 1324 string callbackName = match.QueryParameters[callbackParameterName]; 1325 1326 if (!String.IsNullOrEmpty(callbackName)) 1327 { 1328 javascriptProperty = new JavascriptCallbackResponseMessageProperty 1329 { 1330 CallbackFunctionName = callbackName 1331 }; 1332 OperationContext.Current.OutgoingMessageProperties.Add(JavascriptCallbackResponseMessageProperty.Name, javascriptProperty); 1333 } 1334 } 1335 } 1336 return javascriptProperty; 1337 } 1338 } 1339 } 1340