Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.ServiceModel.Web / System / ServiceModel / Description / WebHttpBehavior.cs
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
34         public WebHttpBehavior()
35         {
36             defaultOutgoingRequestFormat = WebMessageFormat.Xml;
37             defaultOutgoingReplyFormat = WebMessageFormat.Xml;
38             this.defaultBodyStyle = WebMessageBodyStyle.Bare;
39             xmlSerializerManager = new UnwrappedTypesXmlSerializerManager();
40         }
41
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
99         public virtual void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
100         {
101             // do nothing
102         }
103
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
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
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
306         internal virtual string GetWmiTypeName()
307         {
308             return "WebHttpBehavior";
309         }
310
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
324         string IWmiInstanceProvider.GetInstanceType()
325         {
326             return GetWmiTypeName();
327         }
328
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
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
353         protected virtual void ValidateBinding(ServiceEndpoint endpoint)
354         {
355             ValidateIsWebHttpBinding(endpoint, this.GetType().ToString());
356         }
357
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
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
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
409             if (attributes != null && attributes.Length > 0)
410             {
411                 return ((DescriptionAttribute)attributes[0]).Description;
412             }
413             else
414             {
415                 return String.Empty;
416             }
417         }
418
419         internal static bool IsTypedMessage(MessageDescription message)
420         {
421             return (message != null && message.MessageType != null);
422         }
423
424         internal static bool IsUntypedMessage(MessageDescription message)
425         {
426             if (message == null)
427             {
428                 return false;
429             }
430             return (message.Body.ReturnValue != null && message.Body.Parts.Count == 0 && message.Body.ReturnValue.Type == typeof(Message)) ||
431                 (message.Body.ReturnValue == null && message.Body.Parts.Count == 1 && message.Body.Parts[0].Type == typeof(Message));
432         }
433
434         internal static MessageDescription MakeDummyMessageDescription(MessageDirection direction)
435         {
436             MessageDescription messageDescription = new MessageDescription("urn:dummyAction", direction);
437             return messageDescription;
438         }
439
440         internal static bool SupportsJsonFormat(OperationDescription od)
441         {
442             // if the type is XmlSerializable, then we cannot create a json serializer for it
443             DataContractSerializerOperationBehavior dcsob = od.Behaviors.Find<DataContractSerializerOperationBehavior>();
444             return (dcsob != null);
445         }
446
447         internal static void ValidateIsWebHttpBinding(ServiceEndpoint serviceEndpoint, string behaviorName)
448         {
449             Binding binding = serviceEndpoint.Binding;
450             if (binding.Scheme != "http" && binding.Scheme != "https")
451             {
452                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
453                     SR2.GetString(SR2.WCFBindingCannotBeUsedWithUriOperationSelectorBehaviorBadScheme,
454                     serviceEndpoint.Contract.Name, behaviorName)));
455             }
456             if (binding.MessageVersion != MessageVersion.None)
457             {
458                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
459                     SR2.GetString(SR2.WCFBindingCannotBeUsedWithUriOperationSelectorBehaviorBadMessageVersion,
460                     serviceEndpoint.Address.Uri.AbsoluteUri, behaviorName)));
461             }
462             TransportBindingElement transportBindingElement = binding.CreateBindingElements().Find<TransportBindingElement>();
463             if (transportBindingElement != null && !transportBindingElement.ManualAddressing)
464             {
465                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
466                     SR2.GetString(SR2.ManualAddressingCannotBeFalseWithTransportBindingElement,
467                     serviceEndpoint.Address.Uri.AbsoluteUri, behaviorName, transportBindingElement.GetType().Name)));
468             }
469         }
470
471         internal WebMessageBodyStyle GetBodyStyle(OperationDescription od)
472         {
473             WebGetAttribute wga = od.Behaviors.Find<WebGetAttribute>();
474             WebInvokeAttribute wia = od.Behaviors.Find<WebInvokeAttribute>();
475             EnsureOk(wga, wia, od);
476             if (wga != null)
477             {
478                 return wga.GetBodyStyleOrDefault(this.DefaultBodyStyle);
479             }
480             else if (wia != null)
481             {
482                 return wia.GetBodyStyleOrDefault(this.DefaultBodyStyle);
483             }
484             else
485             {
486                 return this.DefaultBodyStyle;
487             }
488         }
489
490         internal IClientMessageFormatter GetDefaultClientFormatter(OperationDescription od, bool useJson, bool isWrapped)
491         {
492             DataContractSerializerOperationBehavior dcsob = od.Behaviors.Find<DataContractSerializerOperationBehavior>();
493             if (useJson)
494             {
495                 if (dcsob == null)
496                 {
497                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(SR2.JsonFormatRequiresDataContract, od.Name, od.DeclaringContract.Name, od.DeclaringContract.Namespace)));
498                 }
499                 return CreateDataContractJsonSerializerOperationFormatter(od, dcsob, isWrapped);
500             }
501             else
502             {
503                 ClientRuntime clientRuntime = new ClientRuntime("name", "");
504                 ClientOperation cop = new ClientOperation(clientRuntime, "dummyClient", "urn:dummy");
505                 cop.Formatter = null;
506
507                 if (dcsob != null)
508                 {
509                     (dcsob as IOperationBehavior).ApplyClientBehavior(od, cop);
510                     return cop.Formatter;
511                 }
512                 XmlSerializerOperationBehavior xsob = od.Behaviors.Find<XmlSerializerOperationBehavior>();
513                 if (xsob != null)
514                 {
515                     xsob = new XmlSerializerOperationBehavior(od, xsob.XmlSerializerFormatAttribute, this.reflector);
516                     (xsob as IOperationBehavior).ApplyClientBehavior(od, cop);
517                     return cop.Formatter;
518                 }
519             }
520             return null;
521         }
522
523         protected virtual void AddClientErrorInspector(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
524         {
525             if (!this.FaultExceptionEnabled)
526             {
527                 clientRuntime.MessageInspectors.Add(new WebFaultClientMessageInspector());
528             }
529             else
530             {
531                 clientRuntime.MessageVersionNoneFaultsEnabled = true;
532             }
533         }
534
535         protected virtual void AddServerErrorHandlers(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
536         {
537             if (!this.FaultExceptionEnabled)
538             {
539                 WebErrorHandler errorHandler = new WebErrorHandler(this, endpoint.Contract, endpointDispatcher.DispatchRuntime.ChannelDispatcher.IncludeExceptionDetailInFaults);
540                 endpointDispatcher.DispatchRuntime.ChannelDispatcher.ErrorHandlers.Add(errorHandler);
541             }
542         }
543
544         protected virtual WebHttpDispatchOperationSelector GetOperationSelector(ServiceEndpoint endpoint)
545         {
546             return new WebHttpDispatchOperationSelector(endpoint);
547         }
548
549         protected virtual QueryStringConverter GetQueryStringConverter(OperationDescription operationDescription)
550         {
551             return new QueryStringConverter();
552         }
553
554         protected virtual IClientMessageFormatter GetReplyClientFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
555         {
556             if (operationDescription.Messages.Count < 2)
557             {
558                 return null;
559             }
560             ValidateBodyParameters(operationDescription, false);
561             Type type;
562             if (TryGetStreamParameterType(operationDescription.Messages[1], operationDescription, false, out type))
563             {
564                 return new HttpStreamFormatter(operationDescription);
565             }
566             if (IsUntypedMessage(operationDescription.Messages[1]))
567             {
568                 return new MessagePassthroughFormatter();
569             }
570             WebMessageBodyStyle style = GetBodyStyle(operationDescription);
571             Type parameterType;
572             if (UseBareReplyFormatter(style, operationDescription, GetResponseFormat(operationDescription), out parameterType))
573             {
574                 return SingleBodyParameterMessageFormatter.CreateXmlAndJsonClientFormatter(operationDescription, parameterType, false, this.xmlSerializerManager);
575             }
576             else
577             {
578                 MessageDescription temp = operationDescription.Messages[0];
579                 operationDescription.Messages[0] = MakeDummyMessageDescription(MessageDirection.Input);
580                 IClientMessageFormatter result;
581                 result = GetDefaultXmlAndJsonClientFormatter(operationDescription, !IsBareResponse(style));
582                 operationDescription.Messages[0] = temp;
583                 return result;
584             }
585         }
586
587         internal virtual bool UseBareReplyFormatter(WebMessageBodyStyle style, OperationDescription operationDescription, WebMessageFormat responseFormat, out Type parameterType)
588         {
589             parameterType = null;
590             return IsBareResponse(style) && TryGetNonMessageParameterType(operationDescription.Messages[1], operationDescription, false, out parameterType);
591         }
592
593         protected virtual IDispatchMessageFormatter GetReplyDispatchFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
594         {
595             if (operationDescription.Messages.Count < 2)
596             {
597                 return null;
598             }
599             ValidateBodyParameters(operationDescription, false);
600             WebMessageFormat responseFormat = GetResponseFormat(operationDescription);
601
602             //  Determine if we should add a json formatter; If the ResponseFormat is json, we always add the json formatter even if the
603             //  operation is XmlSerializerFormat because the formatter constructor throws the exception: "json not valid with XmlSerializerFormat" [Microsoft]
604             bool useJson = (responseFormat == WebMessageFormat.Json || SupportsJsonFormat(operationDescription));
605
606             IDispatchMessageFormatter innerFormatter;
607             Type type;
608
609             if (TryGetStreamParameterType(operationDescription.Messages[1], operationDescription, false, out type))
610             {
611                 innerFormatter = new ContentTypeSettingDispatchMessageFormatter(defaultStreamContentType, new HttpStreamFormatter(operationDescription));
612             }
613             else if (IsUntypedMessage(operationDescription.Messages[1]))
614             {
615                 innerFormatter = new MessagePassthroughFormatter();
616             }
617             else
618             {
619                 Type parameterType;
620                 WebMessageBodyStyle style = GetBodyStyle(operationDescription);
621                 Dictionary<WebMessageFormat, IDispatchMessageFormatter> formatters = new Dictionary<WebMessageFormat, IDispatchMessageFormatter>();
622
623                 if (UseBareReplyFormatter(style, operationDescription, responseFormat, out parameterType))
624                 {
625                     formatters.Add(WebMessageFormat.Xml, SingleBodyParameterMessageFormatter.CreateDispatchFormatter(operationDescription, parameterType, false, false, this.xmlSerializerManager, null));
626                     if (useJson)
627                     {
628                         formatters.Add(WebMessageFormat.Json, SingleBodyParameterMessageFormatter.CreateDispatchFormatter(operationDescription, parameterType, false, true, this.xmlSerializerManager, this.JavascriptCallbackParameterName));
629                     }
630                 }
631                 else
632                 {
633                     MessageDescription temp = operationDescription.Messages[0];
634                     operationDescription.Messages[0] = MakeDummyMessageDescription(MessageDirection.Input);
635                     formatters.Add(WebMessageFormat.Xml, GetDefaultDispatchFormatter(operationDescription, false, !IsBareResponse(style)));
636                     if (useJson)
637                     {
638                         formatters.Add(WebMessageFormat.Json, GetDefaultDispatchFormatter(operationDescription, true, !IsBareResponse(style)));
639                     }
640                     operationDescription.Messages[0] = temp;
641                 }
642                 innerFormatter = new MultiplexingDispatchMessageFormatter(formatters, responseFormat);
643             }
644
645             return innerFormatter;
646         }
647
648         protected virtual IClientMessageFormatter GetRequestClientFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
649         {
650             WebMessageFormat requestFormat = GetRequestFormat(operationDescription);
651             bool useJson = (requestFormat == WebMessageFormat.Json);
652             WebMessageEncodingBindingElement webEncoding = (useJson) ? endpoint.Binding.CreateBindingElements().Find<WebMessageEncodingBindingElement>() : null;
653             IClientMessageFormatter innerFormatter = null;
654
655             // get some validation errors by creating "throwAway" formatter
656
657             // validate that endpoint.Address is not null before accessing the endpoint.Address.Uri. This is to avoid throwing a NullRefException while constructing a UriTemplateClientFormatter
658             if (endpoint.Address == null)
659             {
660                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
661                             SR2.GetString(SR2.ServiceEndpointMustHaveNonNullAddress, typeof(ServiceEndpoint), typeof(ChannelFactory), typeof(WebHttpEndpoint), AddressPropertyName, typeof(ServiceEndpoint))));
662             }
663
664             UriTemplateClientFormatter throwAway = new UriTemplateClientFormatter(operationDescription, null, GetQueryStringConverter(operationDescription), endpoint.Address.Uri, false, endpoint.Contract.Name);
665             int numUriVariables = throwAway.pathMapping.Count + throwAway.queryMapping.Count;
666             bool isStream = false;
667             HideReplyMessage(operationDescription, delegate()
668             {
669                 WebMessageBodyStyle style = GetBodyStyle(operationDescription);
670                 bool isUntypedWhenUriParamsNotConsidered = false;
671                 Effect doBodyFormatter = delegate()
672                 {
673                     if (numUriVariables != 0)
674                     {
675                         EnsureNotUntypedMessageNorMessageContract(operationDescription);
676                     }
677                     // get body formatter
678                     ValidateBodyParameters(operationDescription, true);
679                     IClientMessageFormatter baseFormatter;
680                     Type parameterType;
681                     if (TryGetStreamParameterType(operationDescription.Messages[0], operationDescription, true, out parameterType))
682                     {
683                         isStream = true;
684                         baseFormatter = new HttpStreamFormatter(operationDescription);
685                     }
686                     else if (UseBareRequestFormatter(style, operationDescription, out parameterType))
687                     {
688                         baseFormatter = SingleBodyParameterMessageFormatter.CreateClientFormatter(operationDescription, parameterType, true, useJson, this.xmlSerializerManager);
689                     }
690                     else
691                     {
692                         baseFormatter = GetDefaultClientFormatter(operationDescription, useJson, !IsBareRequest(style));
693                     }
694                     innerFormatter = baseFormatter;
695                     isUntypedWhenUriParamsNotConsidered = IsUntypedMessage(operationDescription.Messages[0]);
696                 };
697                 if (numUriVariables == 0)
698                 {
699                     if (IsUntypedMessage(operationDescription.Messages[0]))
700                     {
701                         ValidateBodyParameters(operationDescription, true);
702                         innerFormatter = new MessagePassthroughFormatter();
703                         isUntypedWhenUriParamsNotConsidered = true;
704                     }
705                     else if (IsTypedMessage(operationDescription.Messages[0]))
706                     {
707                         ValidateBodyParameters(operationDescription, true);
708                         innerFormatter = GetDefaultClientFormatter(operationDescription, useJson, !IsBareRequest(style));
709                     }
710                     else
711                     {
712                         doBodyFormatter();
713                     }
714                 }
715                 else
716                 {
717                     HideRequestUriTemplateParameters(operationDescription, throwAway, delegate()
718                     {
719                         CloneMessageDescriptionsBeforeActing(operationDescription, delegate()
720                         {
721                             doBodyFormatter();
722                         });
723                     });
724                 }
725                 innerFormatter = new UriTemplateClientFormatter(operationDescription, innerFormatter, GetQueryStringConverter(operationDescription), endpoint.Address.Uri, isUntypedWhenUriParamsNotConsidered, endpoint.Contract.Name);
726             });
727             string defaultContentType = GetDefaultContentType(isStream, useJson, webEncoding);
728             if (!string.IsNullOrEmpty(defaultContentType))
729             {
730                 innerFormatter = new ContentTypeSettingClientMessageFormatter(defaultContentType, innerFormatter);
731             }
732             return innerFormatter;
733         }
734
735         protected virtual IDispatchMessageFormatter GetRequestDispatchFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
736         {
737             IDispatchMessageFormatter result = null;
738             // get some validation errors by creating "throwAway" formatter
739             UriTemplateDispatchFormatter throwAway = new UriTemplateDispatchFormatter(operationDescription, null, GetQueryStringConverter(operationDescription), endpoint.Contract.Name, endpoint.Address.Uri);
740             int numUriVariables = throwAway.pathMapping.Count + throwAway.queryMapping.Count;
741             HideReplyMessage(operationDescription, delegate()
742             {
743                 WebMessageBodyStyle style = GetBodyStyle(operationDescription);
744                 Effect doBodyFormatter = delegate()
745                 {
746                     if (numUriVariables != 0)
747                     {
748                         EnsureNotUntypedMessageNorMessageContract(operationDescription);
749                     }
750                     // get body formatter
751                     ValidateBodyParameters(operationDescription, true);
752                     Type type;
753                     if (TryGetStreamParameterType(operationDescription.Messages[0], operationDescription, true, out type))
754                     {
755                         result = new HttpStreamFormatter(operationDescription);
756                     }
757                     else
758                     {
759                         Type parameterType;
760                         if (UseBareRequestFormatter(style, operationDescription, out parameterType))
761                         {
762                             result = SingleBodyParameterMessageFormatter.CreateXmlAndJsonDispatchFormatter(operationDescription, parameterType, true, this.xmlSerializerManager, this.JavascriptCallbackParameterName);
763                         }
764                         else
765                         {
766                             result = GetDefaultXmlAndJsonDispatchFormatter(operationDescription, !IsBareRequest(style));
767                         }
768                     }
769                 };
770                 if (numUriVariables == 0)
771                 {
772                     if (IsUntypedMessage(operationDescription.Messages[0]))
773                     {
774                         ValidateBodyParameters(operationDescription, true);
775                         result = new MessagePassthroughFormatter();
776                     }
777                     else if (IsTypedMessage(operationDescription.Messages[0]))
778                     {
779                         ValidateBodyParameters(operationDescription, true);
780                         result = GetDefaultXmlAndJsonDispatchFormatter(operationDescription, !IsBareRequest(style));
781                     }
782                     else
783                     {
784                         doBodyFormatter();
785                     }
786                 }
787                 else
788                 {
789                     HideRequestUriTemplateParameters(operationDescription, throwAway, delegate()
790                     {
791                         CloneMessageDescriptionsBeforeActing(operationDescription, delegate()
792                         {
793                             doBodyFormatter();
794                         });
795                     });
796                 }
797                 result = new UriTemplateDispatchFormatter(operationDescription, result, GetQueryStringConverter(operationDescription), endpoint.Contract.Name, endpoint.Address.Uri);
798             });
799             return result;
800         }
801
802         static void CloneMessageDescriptionsBeforeActing(OperationDescription operationDescription, Effect effect)
803         {
804             MessageDescription originalRequest = operationDescription.Messages[0];
805             bool thereIsAReply = operationDescription.Messages.Count > 1;
806             MessageDescription originalReply = thereIsAReply ? operationDescription.Messages[1] : null;
807             operationDescription.Messages[0] = originalRequest.Clone();
808             if (thereIsAReply)
809             {
810                 operationDescription.Messages[1] = originalReply.Clone();
811             }
812             effect();
813             operationDescription.Messages[0] = originalRequest;
814             if (thereIsAReply)
815             {
816                 operationDescription.Messages[1] = originalReply;
817             }
818         }
819
820         internal virtual bool UseBareRequestFormatter(WebMessageBodyStyle style, OperationDescription operationDescription, out Type parameterType)
821         {
822             parameterType = null;
823             return IsBareRequest(style) && TryGetNonMessageParameterType(operationDescription.Messages[0], operationDescription, true, out parameterType);
824         }
825
826         static Collection<MessagePartDescription> CloneParts(MessageDescription md)
827         {
828             MessagePartDescriptionCollection bodyParameters = md.Body.Parts;
829             Collection<MessagePartDescription> bodyParametersClone = new Collection<MessagePartDescription>();
830             for (int i = 0; i < bodyParameters.Count; ++i)
831             {
832                 MessagePartDescription copy = bodyParameters[i].Clone();
833                 bodyParametersClone.Add(copy);
834             }
835             return bodyParametersClone;
836         }
837
838         static void EnsureNotUntypedMessageNorMessageContract(OperationDescription operationDescription)
839         {
840             // Called when there are UriTemplate parameters.  UT does not compose with Message
841             // or MessageContract because the SOAP and REST programming models must be uniform here.
842             bool isUnadornedWebGet = false;
843             if (GetWebMethod(operationDescription) == GET && GetWebUriTemplate(operationDescription) == null)
844             {
845                 isUnadornedWebGet = true;
846             }
847             if (IsTypedMessage(operationDescription.Messages[0]))
848             {
849                 if (isUnadornedWebGet)
850                 {
851                     // WebGet will give you UriTemplate parameters by default.
852                     // We need a special error message for this case to prevent confusion.
853                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
854                         SR2.GetString(SR2.GETCannotHaveMCParameter, operationDescription.Name, operationDescription.DeclaringContract.Name, operationDescription.Messages[0].MessageType.Name)));
855                 }
856                 else
857                 {
858                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(
859                         SR2.UTParamsDoNotComposeWithMessageContract, operationDescription.Name, operationDescription.DeclaringContract.Name)));
860                 }
861             }
862
863             if (IsUntypedMessage(operationDescription.Messages[0]))
864             {
865                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(
866                     SR2.UTParamsDoNotComposeWithMessage, operationDescription.Name, operationDescription.DeclaringContract.Name)));
867             }
868         }
869
870         static void EnsureOk(WebGetAttribute wga, WebInvokeAttribute wia, OperationDescription od)
871         {
872             if (wga != null && wia != null)
873             {
874                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
875                     SR2.GetString(SR2.MultipleWebAttributes, od.Name, od.DeclaringContract.Name)));
876             }
877         }
878
879         static void HideReplyMessage(OperationDescription operationDescription, Effect effect)
880         {
881             MessageDescription temp = null;
882             if (operationDescription.Messages.Count > 1)
883             {
884                 temp = operationDescription.Messages[1];
885                 operationDescription.Messages[1] = MakeDummyMessageDescription(MessageDirection.Output);
886             }
887             effect();
888             if (operationDescription.Messages.Count > 1)
889             {
890                 operationDescription.Messages[1] = temp;
891             }
892         }
893
894         static void HideRequestUriTemplateParameters(OperationDescription operationDescription, UriTemplateClientFormatter throwAway, Effect effect)
895         {
896             HideRequestUriTemplateParameters(operationDescription, throwAway.pathMapping, throwAway.queryMapping, effect);
897         }
898
899         internal static void HideRequestUriTemplateParameters(OperationDescription operationDescription, UriTemplateDispatchFormatter throwAway, Effect effect)
900         {
901             HideRequestUriTemplateParameters(operationDescription, throwAway.pathMapping, throwAway.queryMapping, effect);
902         }
903
904         static void HideRequestUriTemplateParameters(OperationDescription operationDescription, Dictionary<int, string> pathMapping, Dictionary<int, KeyValuePair<string, Type>> queryMapping, Effect effect)
905         {
906             // mutate description to hide UriTemplate parameters
907             Collection<MessagePartDescription> originalParts = CloneParts(operationDescription.Messages[0]);
908             Collection<MessagePartDescription> parts = CloneParts(operationDescription.Messages[0]);
909             operationDescription.Messages[0].Body.Parts.Clear();
910             int newIndex = 0;
911             for (int i = 0; i < parts.Count; ++i)
912             {
913                 if (!pathMapping.ContainsKey(i) && !queryMapping.ContainsKey(i))
914                 {
915                     operationDescription.Messages[0].Body.Parts.Add(parts[i]);
916                     parts[i].Index = newIndex++;
917                 }
918             }
919             effect();
920             // unmutate description
921             operationDescription.Messages[0].Body.Parts.Clear();
922             for (int i = 0; i < originalParts.Count; ++i)
923             {
924                 operationDescription.Messages[0].Body.Parts.Add(originalParts[i]);
925             }
926         }
927
928         static bool IsBareRequest(WebMessageBodyStyle style)
929         {
930             return (style == WebMessageBodyStyle.Bare || style == WebMessageBodyStyle.WrappedResponse);
931         }
932
933         static bool IsBareResponse(WebMessageBodyStyle style)
934         {
935             return (style == WebMessageBodyStyle.Bare || style == WebMessageBodyStyle.WrappedRequest);
936         }
937
938         internal static bool TryGetNonMessageParameterType(MessageDescription message, OperationDescription declaringOperation, bool isRequest, out Type type)
939         {
940             type = null;
941             if (message == null)
942             {
943                 return true;
944             }
945             if (IsTypedMessage(message) || IsUntypedMessage(message))
946             {
947                 return false;
948             }
949             if (isRequest)
950             {
951                 if (message.Body.Parts.Count > 1)
952                 {
953                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(SR2.AtMostOneRequestBodyParameterAllowedForUnwrappedMessages, declaringOperation.Name, declaringOperation.DeclaringContract.Name)));
954                 }
955                 if (message.Body.Parts.Count == 1 && message.Body.Parts[0].Type != typeof(void))
956                 {
957                     type = message.Body.Parts[0].Type;
958                 }
959                 return true;
960             }
961             else
962             {
963                 if (message.Body.Parts.Count > 0)
964                 {
965                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(SR2.OnlyReturnValueBodyParameterAllowedForUnwrappedMessages, declaringOperation.Name, declaringOperation.DeclaringContract.Name)));
966                 }
967                 if (message.Body.ReturnValue != null && message.Body.ReturnValue.Type != typeof(void))
968                 {
969                     type = message.Body.ReturnValue.Type;
970                 }
971                 return true;
972             }
973         }
974
975         static bool TryGetStreamParameterType(MessageDescription message, OperationDescription declaringOperation, bool isRequest, out Type type)
976         {
977             type = null;
978             if (message == null || IsTypedMessage(message) || IsUntypedMessage(message))
979             {
980                 return false;
981             }
982             if (isRequest)
983             {
984                 bool hasStream = false;
985                 for (int i = 0; i < message.Body.Parts.Count; ++i)
986                 {
987                     if (typeof(Stream) == message.Body.Parts[i].Type)
988                     {
989                         type = message.Body.Parts[i].Type;
990                         hasStream = true;
991                         break;
992                     }
993
994                 }
995                 if (hasStream && message.Body.Parts.Count > 1)
996                 {
997                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR2.GetString(SR2.AtMostOneRequestBodyParameterAllowedForStream, declaringOperation.Name, declaringOperation.DeclaringContract.Name)));
998                 }
999                 return hasStream;
1000             }
1001             else
1002             {
1003                 // validate that the stream is not an out or ref param
1004                 for (int i = 0; i < message.Body.Parts.Count; ++i)
1005                 {
1006                     if (typeof(Stream) == message.Body.Parts[i].Type)
1007                     {
1008                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR2.GetString(SR2.NoOutOrRefStreamParametersAllowed, message.Body.Parts[i].Name, declaringOperation.Name, declaringOperation.DeclaringContract.Name)));
1009                     }
1010                 }
1011                 if (message.Body.ReturnValue != null && typeof(Stream) == message.Body.ReturnValue.Type)
1012                 {
1013                     // validate that there are no out or ref params
1014                     if (message.Body.Parts.Count > 0)
1015                     {
1016                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR2.GetString(SR2.NoOutOrRefParametersAllowedWithStreamResult, declaringOperation.Name, declaringOperation.DeclaringContract.Name)));
1017                     }
1018                     type = message.Body.ReturnValue.Type;
1019                     return true;
1020                 }
1021
1022                 else
1023                 {
1024                     return false;
1025                 }
1026             }
1027         }
1028
1029         static void ValidateAtMostOneStreamParameter(OperationDescription operation, bool request)
1030         {
1031             Type dummy;
1032             if (request)
1033             {
1034                 TryGetStreamParameterType(operation.Messages[0], operation, true, out dummy);
1035             }
1036             else
1037             {
1038                 if (operation.Messages.Count > 1)
1039                 {
1040                     TryGetStreamParameterType(operation.Messages[1], operation, false, out dummy);
1041                 }
1042             }
1043         }
1044
1045         string GetDefaultContentType(bool isStream, bool useJson, WebMessageEncodingBindingElement webEncoding)
1046         {
1047             if (isStream)
1048             {
1049                 return defaultStreamContentType;
1050             }
1051             else if (useJson)
1052             {
1053                 return JsonMessageEncoderFactory.GetContentType(webEncoding);
1054             }
1055             else
1056             {
1057                 return null;
1058             }
1059         }
1060
1061         IDispatchMessageFormatter GetDefaultDispatchFormatter(OperationDescription od, bool useJson, bool isWrapped)
1062         {
1063             DataContractSerializerOperationBehavior dcsob = od.Behaviors.Find<DataContractSerializerOperationBehavior>();
1064             if (useJson)
1065             {
1066                 if (dcsob == null)
1067                 {
1068                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(SR2.JsonFormatRequiresDataContract, od.Name, od.DeclaringContract.Name, od.DeclaringContract.Namespace)));
1069                 }
1070                 return CreateDataContractJsonSerializerOperationFormatter(od, dcsob, isWrapped);
1071             }
1072             else
1073             {
1074                 EndpointDispatcher dummyED = new EndpointDispatcher(new EndpointAddress("http://localhost/"), "name", "");
1075                 DispatchRuntime dispatchRuntime = dummyED.DispatchRuntime;
1076                 DispatchOperation dop = new DispatchOperation(dispatchRuntime, "dummyDispatch", "urn:dummy");
1077                 dop.Formatter = null;
1078
1079                 if (dcsob != null)
1080                 {
1081                     (dcsob as IOperationBehavior).ApplyDispatchBehavior(od, dop);
1082                     return dop.Formatter;
1083                 }
1084                 XmlSerializerOperationBehavior xsob = od.Behaviors.Find<XmlSerializerOperationBehavior>();
1085                 if (xsob != null)
1086                 {
1087                     xsob = new XmlSerializerOperationBehavior(od, xsob.XmlSerializerFormatAttribute, this.reflector);
1088                     (xsob as IOperationBehavior).ApplyDispatchBehavior(od, dop);
1089                     return dop.Formatter;
1090                 }
1091             }
1092             return null;
1093         }
1094
1095         internal virtual DataContractJsonSerializerOperationFormatter CreateDataContractJsonSerializerOperationFormatter(OperationDescription od, DataContractSerializerOperationBehavior dcsob, bool isWrapped)
1096         {
1097             return new DataContractJsonSerializerOperationFormatter(od, dcsob.MaxItemsInObjectGraph, dcsob.IgnoreExtensionDataObject, dcsob.DataContractSurrogate, isWrapped, false, JavascriptCallbackParameterName);
1098         }
1099
1100         IClientMessageFormatter GetDefaultXmlAndJsonClientFormatter(OperationDescription od, bool isWrapped)
1101         {
1102             IClientMessageFormatter xmlFormatter = GetDefaultClientFormatter(od, false, isWrapped);
1103             if (!SupportsJsonFormat(od))
1104             {
1105                 return xmlFormatter;
1106             }
1107             IClientMessageFormatter jsonFormatter = GetDefaultClientFormatter(od, true, isWrapped);
1108             Dictionary<WebContentFormat, IClientMessageFormatter> map = new Dictionary<WebContentFormat, IClientMessageFormatter>();
1109             map.Add(WebContentFormat.Xml, xmlFormatter);
1110             map.Add(WebContentFormat.Json, jsonFormatter);
1111             // In case there is no format property, the default formatter to use is XML
1112             return new DemultiplexingClientMessageFormatter(map, xmlFormatter);
1113         }
1114
1115         IDispatchMessageFormatter GetDefaultXmlAndJsonDispatchFormatter(OperationDescription od, bool isWrapped)
1116         {
1117             IDispatchMessageFormatter xmlFormatter = GetDefaultDispatchFormatter(od, false, isWrapped);
1118             if (!SupportsJsonFormat(od))
1119             {
1120                 return xmlFormatter;
1121             }
1122             IDispatchMessageFormatter jsonFormatter = GetDefaultDispatchFormatter(od, true, isWrapped);
1123             Dictionary<WebContentFormat, IDispatchMessageFormatter> map = new Dictionary<WebContentFormat, IDispatchMessageFormatter>();
1124             map.Add(WebContentFormat.Xml, xmlFormatter);
1125             map.Add(WebContentFormat.Json, jsonFormatter);
1126             return new DemultiplexingDispatchMessageFormatter(map, xmlFormatter);
1127         }
1128
1129         internal WebMessageFormat GetRequestFormat(OperationDescription od)
1130         {
1131             WebGetAttribute wga = od.Behaviors.Find<WebGetAttribute>();
1132             WebInvokeAttribute wia = od.Behaviors.Find<WebInvokeAttribute>();
1133             EnsureOk(wga, wia, od);
1134             if (wga != null)
1135             {
1136                 return wga.IsRequestFormatSetExplicitly ? wga.RequestFormat : this.DefaultOutgoingRequestFormat;
1137             }
1138             else if (wia != null)
1139             {
1140                 return wia.IsRequestFormatSetExplicitly ? wia.RequestFormat : this.DefaultOutgoingRequestFormat;
1141             }
1142             else
1143             {
1144                 return this.DefaultOutgoingRequestFormat;
1145             }
1146         }
1147
1148         internal WebMessageFormat GetResponseFormat(OperationDescription od)
1149         {
1150             WebGetAttribute wga = od.Behaviors.Find<WebGetAttribute>();
1151             WebInvokeAttribute wia = od.Behaviors.Find<WebInvokeAttribute>();
1152             EnsureOk(wga, wia, od);
1153             if (wga != null)
1154             {
1155                 return wga.IsResponseFormatSetExplicitly ? wga.ResponseFormat : this.DefaultOutgoingResponseFormat;
1156             }
1157             else if (wia != null)
1158             {
1159                 return wia.IsResponseFormatSetExplicitly ? wia.ResponseFormat : this.DefaultOutgoingResponseFormat;
1160             }
1161             else
1162             {
1163                 return this.DefaultOutgoingResponseFormat;
1164             }
1165         }
1166
1167         void ValidateBodyParameters(OperationDescription operation, bool request)
1168         {
1169             string method = GetWebMethod(operation);
1170             if (request)
1171             {
1172                 ValidateGETHasNoBody(operation, method);
1173             }
1174             // validate that if bare is chosen for request/response, then at most 1 parameter is possible
1175             ValidateBodyStyle(operation, request);
1176             // validate if the request or response body is a stream, no other body parameters
1177             // can be specified
1178             ValidateAtMostOneStreamParameter(operation, request);
1179         }
1180
1181         void ValidateBodyStyle(OperationDescription operation, bool request)
1182         {
1183             WebMessageBodyStyle style = GetBodyStyle(operation);
1184             Type dummy;
1185             if (request && IsBareRequest(style))
1186             {
1187                 TryGetNonMessageParameterType(operation.Messages[0], operation, true, out dummy);
1188             }
1189             if (!request && operation.Messages.Count > 1 && IsBareResponse(style))
1190             {
1191                 TryGetNonMessageParameterType(operation.Messages[1], operation, false, out dummy);
1192             }
1193         }
1194
1195         void ValidateGETHasNoBody(OperationDescription operation, string method)
1196         {
1197             if (method == GET)
1198             {
1199                 if (!IsUntypedMessage(operation.Messages[0]) && operation.Messages[0].Body.Parts.Count != 0)
1200                 {
1201                     if (!IsTypedMessage(operation.Messages[0]))
1202                     {
1203                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
1204                             SR2.GetString(SR2.GETCannotHaveBody, operation.Name, operation.DeclaringContract.Name, operation.Messages[0].Body.Parts[0].Name)));
1205                     }
1206                     else
1207                     {
1208                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
1209                             SR2.GetString(SR2.GETCannotHaveMCParameter, operation.Name, operation.DeclaringContract.Name, operation.Messages[0].MessageType.Name)));
1210                     }
1211                 }
1212             }
1213         }
1214
1215         void ValidateContract(ServiceEndpoint endpoint)
1216         {
1217             foreach (OperationDescription od in endpoint.Contract.Operations)
1218             {
1219                 ValidateNoOperationHasEncodedXmlSerializer(od);
1220                 ValidateNoMessageContractHeaders(od.Messages[0], od.Name, endpoint.Contract.Name);
1221                 ValidateNoBareMessageContractWithMultipleParts(od.Messages[0], od.Name, endpoint.Contract.Name);
1222                 ValidateNoMessageContractWithStream(od.Messages[0], od.Name, endpoint.Contract.Name);
1223                 if (od.Messages.Count > 1)
1224                 {
1225                     ValidateNoMessageContractHeaders(od.Messages[1], od.Name, endpoint.Contract.Name);
1226                     ValidateNoBareMessageContractWithMultipleParts(od.Messages[1], od.Name, endpoint.Contract.Name);
1227                     ValidateNoMessageContractWithStream(od.Messages[1], od.Name, endpoint.Contract.Name);
1228                 }
1229             }
1230         }
1231
1232         internal static bool IsXmlSerializerFaultFormat(OperationDescription operationDescription)
1233         {
1234             XmlSerializerOperationBehavior xsob = operationDescription.Behaviors.Find<XmlSerializerOperationBehavior>();
1235             return (xsob != null && xsob.XmlSerializerFormatAttribute.SupportFaults);
1236         }
1237
1238         void ValidateNoMessageContractWithStream(MessageDescription md, string opName, string contractName)
1239         {
1240             if (IsTypedMessage(md))
1241             {
1242                 foreach (MessagePartDescription description in md.Body.Parts)
1243                 {
1244                     if (description.Type == typeof(Stream))
1245                     {
1246                         throw System.ServiceModel.DiagnosticUtility.ExceptionUtility.ThrowHelperError(
1247                             new InvalidOperationException(System.ServiceModel.SR2.GetString(System.ServiceModel.SR2.StreamBodyMemberNotSupported, this.GetType().ToString(), contractName, opName, md.MessageType.ToString(), description.Name)));
1248                     }
1249                 }
1250             }
1251         }
1252
1253         void ValidateNoOperationHasEncodedXmlSerializer(OperationDescription od)
1254         {
1255             XmlSerializerOperationBehavior xsob = od.Behaviors.Find<XmlSerializerOperationBehavior>();
1256             if (xsob != null && (xsob.XmlSerializerFormatAttribute.Style == OperationFormatStyle.Rpc || xsob.XmlSerializerFormatAttribute.IsEncoded))
1257             {
1258                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(SR2.RpcEncodedNotSupportedForNoneMessageVersion, od.Name, od.DeclaringContract.Name, od.DeclaringContract.Namespace)));
1259             }
1260         }
1261
1262         void ValidateNoBareMessageContractWithMultipleParts(MessageDescription md, string opName, string contractName)
1263         {
1264             if (IsTypedMessage(md) && md.Body.WrapperName == null)
1265             {
1266                 if (md.Body.Parts.Count > 1)
1267                 {
1268                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
1269                         SR2.GetString(SR2.InvalidMessageContractWithoutWrapperName, opName, contractName, md.MessageType)));
1270                 }
1271                 if (md.Body.Parts.Count == 1 && md.Body.Parts[0].Multiple)
1272                 {
1273                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(SR2.MCAtMostOneRequestBodyParameterAllowedForUnwrappedMessages, opName, contractName, md.MessageType)));
1274                 }
1275             }
1276         }
1277
1278         void ValidateNoMessageContractHeaders(MessageDescription md, string opName, string contractName)
1279         {
1280             if (md.Headers.Count != 0)
1281             {
1282                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
1283                     SR2.GetString(SR2.InvalidMethodWithSOAPHeaders, opName, contractName)));
1284             }
1285         }
1286
1287         internal class MessagePassthroughFormatter : IClientMessageFormatter, IDispatchMessageFormatter
1288         {
1289             public object DeserializeReply(Message message, object[] parameters)
1290             {
1291                 return message;
1292             }
1293
1294             public void DeserializeRequest(Message message, object[] parameters)
1295             {
1296                 parameters[0] = message;
1297             }
1298
1299             public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
1300             {
1301                 return result as Message;
1302             }
1303
1304             public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
1305             {
1306                 return parameters[0] as Message;
1307             }
1308         }
1309
1310         static internal JavascriptCallbackResponseMessageProperty TrySetupJavascriptCallback(string callbackParameterName)
1311         {
1312             JavascriptCallbackResponseMessageProperty javascriptProperty = null;
1313             if (!String.IsNullOrEmpty(callbackParameterName) &&
1314                 !OperationContext.Current.OutgoingMessageProperties.TryGetValue<JavascriptCallbackResponseMessageProperty>(JavascriptCallbackResponseMessageProperty.Name, out javascriptProperty))
1315             {
1316                 UriTemplateMatch match = WebOperationContext.Current.IncomingRequest.UriTemplateMatch;
1317                 if (match != null &&
1318                     match.QueryParameters.AllKeys.Contains(callbackParameterName))
1319                 {
1320                     string callbackName = match.QueryParameters[callbackParameterName];
1321
1322                     if (!String.IsNullOrEmpty(callbackName))
1323                     {
1324                         javascriptProperty = new JavascriptCallbackResponseMessageProperty
1325                         {
1326                             CallbackFunctionName = callbackName
1327                         };
1328                         OperationContext.Current.OutgoingMessageProperties.Add(JavascriptCallbackResponseMessageProperty.Name, javascriptProperty);
1329                     }
1330                 }
1331             }
1332             return javascriptProperty;
1333         }
1334     }
1335 }