New tests.
[mono.git] / mcs / class / System.ServiceModel.Web / System.ServiceModel.Description / WebHttpBehavior.cs
1 //
2 // WebHttpBehavior.cs
3 //
4 // Author:
5 //      Atsushi Enomoto  <atsushi@ximian.com>
6 //
7 // Copyright (C) 2008 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 using System;
29 using System.ServiceModel;
30 using System.ServiceModel.Channels;
31 using System.ServiceModel.Dispatcher;
32 using System.ServiceModel.Web;
33
34 namespace System.ServiceModel.Description
35 {
36
37         internal static class WebHttpBehaviorExtensions
38         {
39                 public static WebAttributeInfo GetWebAttributeInfo (this OperationDescription od)
40                 {
41 #if NET_2_1
42                         var mi = od.BeginMethod ?? od.SyncMethod;
43                         var atts = mi.GetCustomAttributes (typeof (WebGetAttribute), true);
44                         if (atts.Length == 1)
45                                 return ((WebGetAttribute) atts [0]).Info;
46                         atts = mi.GetCustomAttributes (typeof (WebInvokeAttribute), true);
47                         if (atts.Length == 1)
48                                 return ((WebInvokeAttribute) atts [0]).Info;
49                         return null;
50 #else
51                         foreach (IOperationBehavior ob in od.Behaviors) {
52                                 WebAttributeInfo info = null;
53                                 var wg = ob as WebGetAttribute;
54                                 if (wg != null)
55                                         return wg.Info;
56                                 var wi = ob as WebInvokeAttribute;
57                                 if (wi != null)
58                                         return wi.Info;
59                         }
60                         return new WebGetAttribute ().Info; // blank one
61 #endif
62                 }
63         }
64
65         public class WebHttpBehavior
66 #if !NET_2_1
67          : IEndpointBehavior
68 #endif
69         {
70                 public WebHttpBehavior ()
71                 {
72                         DefaultBodyStyle = WebMessageBodyStyle.Bare;
73                         DefaultOutgoingRequestFormat = WebMessageFormat.Xml;
74                         DefaultOutgoingResponseFormat = WebMessageFormat.Xml;
75                 }
76
77                 public virtual WebMessageBodyStyle DefaultBodyStyle { get; set; }
78
79                 public virtual WebMessageFormat DefaultOutgoingRequestFormat { get; set; }
80
81                 public virtual WebMessageFormat DefaultOutgoingResponseFormat { get; set; }
82
83                 public virtual void AddBindingParameters (ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
84                 {
85                         // nothing
86                 }
87
88                 [MonoTODO]
89                 protected virtual void AddClientErrorInspector (ServiceEndpoint endpoint, ClientRuntime clientRuntime)
90                 {
91                         // clientRuntime.MessageInspectors.Add (something);
92                 }
93
94 #if !NET_2_1
95                 [MonoTODO]
96                 protected virtual void AddServerErrorHandlers (ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
97                 {
98                         // endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add (something);
99                 }
100 #endif
101
102                 public virtual void ApplyClientBehavior (ServiceEndpoint endpoint, ClientRuntime clientRuntime)
103                 {
104                         AddClientErrorInspector (endpoint, clientRuntime);
105 #if MOONLIGHT
106                         throw new NotSupportedException ("Due to the lack of ClientRuntime.Operations, Silverlight cannot support this binding.");
107 #else
108                         foreach (ClientOperation oper in clientRuntime.Operations) {
109                                 var req = GetRequestClientFormatter (endpoint.Contract.Operations.Find (oper.Name), endpoint);
110                                 var res = GetReplyClientFormatter (endpoint.Contract.Operations.Find (oper.Name), endpoint);
111                                 oper.Formatter = new ClientPairFormatter (req, res);
112                         }
113 #endif
114                 }
115
116 #if !NET_2_1
117                 public virtual void ApplyDispatchBehavior (ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
118                 {
119                         endpointDispatcher.DispatchRuntime.OperationSelector = GetOperationSelector (endpoint);
120                         // FIXME: get HostNameComparisonMode from WebHttpBinding by some means.
121                         endpointDispatcher.FilterPriority = 1; // It is to take higher priority than that of ServiceMetadataExtension (whose URL likely conflicts with this one).
122                         endpointDispatcher.AddressFilter = new PrefixEndpointAddressMessageFilter (endpoint.Address);
123                         endpointDispatcher.ContractFilter = new MatchAllMessageFilter ();
124                         AddServerErrorHandlers (endpoint, endpointDispatcher);
125
126                         foreach (DispatchOperation oper in endpointDispatcher.DispatchRuntime.Operations) {
127                                 var req = GetRequestDispatchFormatter (endpoint.Contract.Operations.Find (oper.Name), endpoint);
128                                 var res = GetReplyDispatchFormatter (endpoint.Contract.Operations.Find (oper.Name), endpoint);
129                                 oper.Formatter = new DispatchPairFormatter (req, res);
130                         }
131                 }
132 #endif
133
134                 internal class ClientPairFormatter : IClientMessageFormatter
135                 {
136                         public ClientPairFormatter (IClientMessageFormatter request, IClientMessageFormatter reply)
137                         {
138                                 this.request = request;
139                                 this.reply = reply;
140                         }
141
142                         IClientMessageFormatter request, reply;
143
144                         public Message SerializeRequest (MessageVersion messageVersion, object [] parameters)
145                         {
146                                 return request.SerializeRequest (messageVersion, parameters);
147                         }
148
149                         public object DeserializeReply (Message message, object [] parameters)
150                         {
151                                 return reply.DeserializeReply (message, parameters);
152                         }
153                 }
154
155 #if !NET_2_1
156                 internal class DispatchPairFormatter : IDispatchMessageFormatter
157                 {
158                         public DispatchPairFormatter (IDispatchMessageFormatter request, IDispatchMessageFormatter reply)
159                         {
160                                 this.request = request;
161                                 this.reply = reply;
162                         }
163
164                         IDispatchMessageFormatter request;
165                         IDispatchMessageFormatter reply;
166
167                         public void DeserializeRequest (Message message, object [] parameters)
168                         {
169                                 request.DeserializeRequest (message, parameters);
170                         }
171
172                         public Message SerializeReply (MessageVersion messageVersion, object [] parameters, object result)
173                         {
174                                 return reply.SerializeReply (messageVersion, parameters, result);
175                         }
176                 }
177
178                 protected virtual WebHttpDispatchOperationSelector GetOperationSelector (ServiceEndpoint endpoint)
179                 {
180                         return new WebHttpDispatchOperationSelector (endpoint);
181                 }
182 #endif
183
184                 protected virtual QueryStringConverter GetQueryStringConverter (OperationDescription operationDescription)
185                 {
186                         return new QueryStringConverter ();
187                 }
188
189                 protected virtual IClientMessageFormatter GetReplyClientFormatter (OperationDescription operationDescription, ServiceEndpoint endpoint)
190                 {
191                         return new WebMessageFormatter.ReplyClientFormatter (operationDescription, endpoint, GetQueryStringConverter (operationDescription), this);
192                 }
193
194 #if !NET_2_1
195                 protected virtual IDispatchMessageFormatter GetReplyDispatchFormatter (OperationDescription operationDescription, ServiceEndpoint endpoint)
196                 {
197                         return new WebMessageFormatter.ReplyDispatchFormatter (operationDescription, endpoint, GetQueryStringConverter (operationDescription), this);
198                 }
199 #endif
200
201                 protected virtual IClientMessageFormatter GetRequestClientFormatter (OperationDescription operationDescription, ServiceEndpoint endpoint)
202                 {
203                         return new WebMessageFormatter.RequestClientFormatter (operationDescription, endpoint, GetQueryStringConverter (operationDescription), this);
204                 }
205
206 #if !NET_2_1
207                 protected virtual IDispatchMessageFormatter GetRequestDispatchFormatter (OperationDescription operationDescription, ServiceEndpoint endpoint)
208                 {
209                         return new WebMessageFormatter.RequestDispatchFormatter (operationDescription, endpoint, GetQueryStringConverter (operationDescription), this);
210                 }
211 #endif
212
213                 [MonoTODO ("check UriTemplate validity")]
214                 public virtual void Validate (ServiceEndpoint endpoint)
215                 {
216                         if (endpoint == null)
217                                 throw new ArgumentNullException ("endpoint");
218
219                         foreach (var oper in endpoint.Contract.Operations) {
220                                 var wai = oper.GetWebAttributeInfo ();
221                                 if (wai.Method == "GET")
222                                         continue;
223                                 var style = wai != null && wai.IsBodyStyleSetExplicitly ? wai.BodyStyle : DefaultBodyStyle;
224                                 foreach (var msg in oper.Messages)
225                                         switch (style) {
226                                         case WebMessageBodyStyle.Wrapped:
227                                                 continue;
228                                         case WebMessageBodyStyle.WrappedRequest:
229                                                 if (msg.Direction == MessageDirection.Output)
230                                                         continue;
231                                                 goto case WebMessageBodyStyle.Bare;
232                                         case WebMessageBodyStyle.WrappedResponse:
233                                                 if (msg.Direction == MessageDirection.Input)
234                                                         continue;
235                                                 goto case WebMessageBodyStyle.Bare;
236                                         case WebMessageBodyStyle.Bare:
237                                         default:
238                                                 if (msg.Body.Parts.Count > 1)
239                                                         throw new InvalidOperationException (String.Format ("{0} message on operation '{1}' has multiple parameters which is not allowed when the operation indicates no wrapper element. BodyStyle must be 'wrapped' on the operation WebInvoke/WebGet attribute.", msg.Direction, oper.Name));
240                                                 break;
241                                         }
242                         }
243
244                         ValidateBinding (endpoint);
245                 }
246
247                 protected virtual void ValidateBinding (ServiceEndpoint endpoint)
248                 {
249                         switch (endpoint.Binding.Scheme) {
250                         case "http":
251                         case "https":
252                                 break;
253                         default:
254                                 throw new InvalidOperationException ("Only http and https are allowed for WebHttpBehavior");
255                         }
256                         if (!endpoint.Binding.MessageVersion.Equals (MessageVersion.None))
257                                 throw new InvalidOperationException ("Only MessageVersion.None is allowed for WebHttpBehavior");
258                         if (!endpoint.Binding.CreateBindingElements ().Find<TransportBindingElement> ().ManualAddressing)
259                                 throw new InvalidOperationException ("ManualAddressing in the transport binding element in the binding must be true for WebHttpBehavior");
260                 }
261         }
262 }