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