* HttpGetTypeStubInfo.cs, HttpPostTypeStubInfo.cs, HttpSimpleTypeStubInfo.cs,
[mono.git] / mcs / class / System.Web.Services / System.Web.Services.Protocols / Methods.cs
1 //
2 // Methods.cs: Information about a method and its mapping to a SOAP web service.
3 //
4 // Author:
5 //   Miguel de Icaza
6 //   Lluis Sanchez Gual (lluis@ximian.com)
7 //
8 // (C) 2003 Ximian, Inc.
9 //
10 // TODO:
11 //    
12 //
13
14 using System.Reflection;
15 using System.Collections;
16 using System.Xml;
17 using System.Xml.Serialization;
18 using System.Web.Services;
19 using System.Web.Services.Description;
20
21 namespace System.Web.Services.Protocols {
22
23         //
24         // This class represents all the information we extract from a MethodInfo
25         // in the SoapHttpClientProtocol derivative stub class
26         //
27         internal class SoapMethodStubInfo : MethodStubInfo
28         {
29                 internal string Action;
30                 internal string Binding;
31
32                 // The name/namespace of the request 
33                 internal string RequestName;
34                 internal string RequestNamespace;
35
36                 // The name/namespace of the response.
37                 internal string ResponseName;
38                 internal string ResponseNamespace;
39                 
40                 internal bool OneWay;
41                 internal SoapParameterStyle ParameterStyle;
42                 internal SoapBindingStyle SoapBindingStyle;
43                 internal SoapBindingUse Use;
44
45                 internal HeaderInfo[] Headers;
46                 internal SoapExtensionRuntimeConfig[] SoapExtensions;
47                 
48                 internal XmlMembersMapping InputMembersMapping;
49                 internal XmlMembersMapping OutputMembersMapping;
50                 
51                 private int requestSerializerId;
52                 private int responseSerializerId;
53                 
54                 internal XmlSerializer RequestSerializer
55                 {
56                         get { return TypeStub.GetSerializer (requestSerializerId); }
57                 }
58                 
59                 internal XmlSerializer ResponseSerializer
60                 {
61                         get { return TypeStub.GetSerializer (responseSerializerId); }
62                 }
63
64                 //
65                 // Constructor
66                 //
67                 public SoapMethodStubInfo (TypeStubInfo typeStub, LogicalMethodInfo source, object kind, XmlReflectionImporter xmlImporter, SoapReflectionImporter soapImporter)
68                 : base (typeStub, source)
69                 {
70                         SoapTypeStubInfo parent = (SoapTypeStubInfo) typeStub;
71                         XmlElementAttribute optional_ns = null;
72
73                         if (kind == null) {
74                                 Use = parent.Use;
75                                 RequestName = "";
76                                 RequestNamespace = "";
77                                 ResponseName = "";
78                                 ResponseNamespace = "";
79                                 ParameterStyle = parent.ParameterStyle;
80                                 SoapBindingStyle = parent.SoapBindingStyle;
81                                 OneWay = false;
82                         }
83                         else if (kind is SoapDocumentMethodAttribute){
84                                 SoapDocumentMethodAttribute dma = (SoapDocumentMethodAttribute) kind;
85                                 
86                                 Use = dma.Use;
87                                 if (Use == SoapBindingUse.Default) {
88                                         if (parent.SoapBindingStyle == SoapBindingStyle.Document)
89                                                 Use = parent.Use;
90                                         else
91                                                 Use = SoapBindingUse.Literal;
92                                 }
93                                 
94                                 Action = dma.Action;
95                                 Binding = dma.Binding;
96                                 RequestName = dma.RequestElementName;
97                                 RequestNamespace = dma.RequestNamespace;
98                                 ResponseName = dma.ResponseElementName;
99                                 ResponseNamespace = dma.ResponseNamespace;
100                                 ParameterStyle = dma.ParameterStyle;
101                                 if (ParameterStyle == SoapParameterStyle.Default)
102                                         ParameterStyle = parent.ParameterStyle;
103                                 OneWay = dma.OneWay;
104                                 SoapBindingStyle = SoapBindingStyle.Document;
105                         } else {
106                                 SoapRpcMethodAttribute rma = (SoapRpcMethodAttribute) kind;
107                                 Use = SoapBindingUse.Encoded;   // RPC always use encoded
108
109                                 Action = rma.Action;
110                                 Binding = rma.Binding;
111                                 RequestName = rma.RequestElementName;
112                                 RequestNamespace = rma.RequestNamespace;
113                                 ResponseNamespace = rma.ResponseNamespace;
114                                 ResponseName = rma.ResponseElementName;
115                                 ParameterStyle = SoapParameterStyle.Wrapped;
116                                 OneWay = rma.OneWay;
117                                 SoapBindingStyle = SoapBindingStyle.Rpc;
118
119                                 // For RPC calls, make all arguments be part of the empty namespace
120                                 optional_ns = new XmlElementAttribute ();
121                                 optional_ns.Namespace = "";
122                         }
123
124                         if (OneWay){
125                                 if (source.ReturnType != typeof (void))
126                                         throw new Exception ("OneWay methods should not have a return value");
127                                 if (source.OutParameters.Length != 0)
128                                         throw new Exception ("OneWay methods should not have out/ref parameters");
129                         }
130                         
131                         if (RequestNamespace == "") RequestNamespace = parent.LogicalType.GetWebServiceNamespace (Use);
132                         if (ResponseNamespace == "") ResponseNamespace = parent.LogicalType.GetWebServiceNamespace (Use);
133                         if (RequestName == "") RequestName = Name;
134                         if (ResponseName == "") ResponseName = Name + "Response";
135                         if (Binding == null || Binding == "") Binding = parent.DefaultBinding;
136                         else if (parent.GetBinding (Binding) == null) throw new InvalidOperationException ("Type '" + parent.Type + "' is missing WebServiceBinding attribute that defines a binding named '" + Binding + "'");
137                                 
138                         if (Action == null || Action == "")
139                                 Action = RequestNamespace.EndsWith("/") ? (RequestNamespace + Name) : (RequestNamespace + "/" + Name);
140                         
141                         bool hasWrappingElem = (ParameterStyle == SoapParameterStyle.Wrapped);
142                         
143                         XmlReflectionMember [] in_members = BuildRequestReflectionMembers (optional_ns);
144                         XmlReflectionMember [] out_members = BuildResponseReflectionMembers (optional_ns);
145
146                         if (Use == SoapBindingUse.Literal) {
147                                 InputMembersMapping = xmlImporter.ImportMembersMapping (RequestName, RequestNamespace, in_members, hasWrappingElem);
148                                 OutputMembersMapping = xmlImporter.ImportMembersMapping (ResponseName, ResponseNamespace, out_members, hasWrappingElem);
149                         }
150                         else {
151                                 InputMembersMapping = soapImporter.ImportMembersMapping (RequestName, RequestNamespace, in_members, hasWrappingElem, true);
152                                 OutputMembersMapping = soapImporter.ImportMembersMapping (ResponseName, ResponseNamespace, out_members, hasWrappingElem, true);
153                         }
154
155                         requestSerializerId = parent.RegisterSerializer (InputMembersMapping);
156                         responseSerializerId = parent.RegisterSerializer (OutputMembersMapping);
157
158                         object[] o = source.GetCustomAttributes (typeof (SoapHeaderAttribute));
159                         Headers = new HeaderInfo[o.Length];
160                         for (int i = 0; i < o.Length; i++) {
161                                 SoapHeaderAttribute att = (SoapHeaderAttribute) o[i];
162                                 MemberInfo[] mems = source.DeclaringType.GetMember (att.MemberName);
163                                 if (mems.Length == 0) throw new InvalidOperationException ("Member " + att.MemberName + " not found in class " + source.DeclaringType.FullName);
164                                 
165                                 Type headerType = (mems[0] is FieldInfo) ? ((FieldInfo)mems[0]).FieldType : ((PropertyInfo)mems[0]).PropertyType;
166                                 Headers [i] = new HeaderInfo (mems[0], att);
167                                 parent.RegisterHeaderType (headerType, Use);
168                         }
169
170                         SoapExtensions = SoapExtension.GetMethodExtensions (source);
171                 }
172
173                 XmlReflectionMember [] BuildRequestReflectionMembers (XmlElementAttribute optional_ns)
174                 {
175                         ParameterInfo [] input = MethodInfo.InParameters;
176                         XmlReflectionMember [] in_members = new XmlReflectionMember [input.Length];
177
178                         for (int i = 0; i < input.Length; i++)
179                         {
180                                 XmlReflectionMember m = new XmlReflectionMember ();
181                                 m.IsReturnValue = false;
182                                 m.MemberName = input [i].Name;
183                                 m.MemberType = input [i].ParameterType;
184
185                                 m.XmlAttributes = new XmlAttributes (input[i]);
186                                 m.SoapAttributes = new SoapAttributes (input[i]);
187
188                                 if (m.MemberType.IsByRef)
189                                         m.MemberType = m.MemberType.GetElementType ();
190                                 if (optional_ns != null)
191                                         m.XmlAttributes.XmlElements.Add (optional_ns);
192                                 in_members [i] = m;
193                         }
194                         return in_members;
195                 }
196                 
197                 XmlReflectionMember [] BuildResponseReflectionMembers (XmlElementAttribute optional_ns)
198                 {
199                         ParameterInfo [] output = MethodInfo.OutParameters;
200                         bool has_return_value = !(OneWay || MethodInfo.ReturnType == typeof (void));
201                         XmlReflectionMember [] out_members = new XmlReflectionMember [(has_return_value ? 1 : 0) + output.Length];
202                         XmlReflectionMember m;
203                         int idx = 0;
204
205                         if (has_return_value)
206                         {
207                                 m = new XmlReflectionMember ();
208                                 m.IsReturnValue = true;
209                                 m.MemberName = RequestName + "Result";
210                                 m.MemberType = MethodInfo.ReturnType;
211
212                                 m.XmlAttributes = new XmlAttributes (MethodInfo.ReturnTypeCustomAttributeProvider);
213                                 m.SoapAttributes = new SoapAttributes (MethodInfo.ReturnTypeCustomAttributeProvider);
214
215                                 if (optional_ns != null)
216                                         m.XmlAttributes.XmlElements.Add (optional_ns);
217                                 idx++;
218                                 out_members [0] = m;
219                         }
220                         
221                         for (int i = 0; i < output.Length; i++)
222                         {
223                                 m = new XmlReflectionMember ();
224                                 m.IsReturnValue = false;
225                                 m.MemberName = output [i].Name;
226                                 m.MemberType = output [i].ParameterType;
227                                 m.XmlAttributes = new XmlAttributes (output[i]);
228                                 m.SoapAttributes = new SoapAttributes (output[i]);
229
230                                 if (m.MemberType.IsByRef)
231                                         m.MemberType = m.MemberType.GetElementType ();
232                                 if (optional_ns != null)
233                                         m.XmlAttributes.XmlElements.Add (optional_ns);
234                                 out_members [i + idx] = m;
235                         }
236                         return out_members;
237                 }
238
239                 public HeaderInfo GetHeaderInfo (Type headerType)
240                 {
241                         foreach (HeaderInfo headerInfo in Headers)
242                                 if (headerInfo.HeaderType == headerType) return headerInfo;
243                         return null;
244                 }
245         }
246
247         internal class HeaderInfo
248         {
249                 internal MemberInfo Member;
250                 internal SoapHeaderAttribute AttributeInfo;
251                 internal Type HeaderType;
252
253                 public HeaderInfo (MemberInfo member, SoapHeaderAttribute attributeInfo)
254                 {
255                         Member = member;
256                         AttributeInfo = attributeInfo;
257                         if (Member is PropertyInfo) HeaderType = ((PropertyInfo)Member).PropertyType;
258                         else HeaderType = ((FieldInfo)Member).FieldType;
259                 }
260                 
261                 public object GetHeaderValue (object ob)
262                 {
263                         if (Member is PropertyInfo) return ((PropertyInfo)Member).GetValue (ob, null);
264                         else return ((FieldInfo)Member).GetValue (ob);
265                 }
266
267                 public void SetHeaderValue (object ob, object value)
268                 {
269                         if (Member is PropertyInfo) ((PropertyInfo)Member).SetValue (ob, value, null);
270                         else ((FieldInfo)Member).SetValue (ob, value);
271                 }
272
273                 public SoapHeaderDirection Direction
274                 {
275                         get { return AttributeInfo.Direction; }
276                 }
277         }
278
279         internal class Fault
280         {
281                 public Fault () {}
282
283                 public Fault (SoapException ex) 
284                 {
285                         faultcode = ex.Code;
286                         faultstring = ex.Message;
287                         faultactor = ex.Actor;
288                         detail = ex.Detail;
289                 }
290
291                 public XmlQualifiedName faultcode;
292                 public string faultstring;
293                 public string faultactor;
294                 
295                 [SoapIgnore]
296                 public XmlNode detail;
297         }
298         
299         //
300         // Holds the metadata loaded from the type stub, as well as
301         // the metadata for all the methods in the type
302         //
303         internal class SoapTypeStubInfo : TypeStubInfo
304         {
305                 Hashtable[] header_serializers = new Hashtable [3];
306                 Hashtable[] header_serializers_byname = new Hashtable [3];
307
308                 // Precomputed
309                 internal SoapParameterStyle      ParameterStyle;
310                 internal SoapServiceRoutingStyle RoutingStyle;
311                 internal SoapBindingUse          Use;
312                 internal int                     faultSerializerLitId = -1;
313                 internal int                     faultSerializerEncId = -1;
314                 internal SoapExtensionRuntimeConfig[][] SoapExtensions;
315                 internal SoapBindingStyle SoapBindingStyle;
316                 internal XmlReflectionImporter  xmlImporter;
317                 internal SoapReflectionImporter soapImporter;
318
319                 public SoapTypeStubInfo (LogicalTypeInfo logicalTypeInfo)
320                 : base (logicalTypeInfo)
321                 {
322                         xmlImporter = new XmlReflectionImporter ();
323                         soapImporter = new SoapReflectionImporter ();
324                         
325                         object [] o;
326
327                         o = Type.GetCustomAttributes (typeof (WebServiceBindingAttribute), false);
328                         foreach (WebServiceBindingAttribute at in o)
329                                 Bindings.Add (new BindingInfo (at, LogicalType.WebServiceNamespace));
330
331                         o = Type.GetCustomAttributes (typeof (SoapDocumentServiceAttribute), false);
332                         if (o.Length == 1){
333                                 SoapDocumentServiceAttribute a = (SoapDocumentServiceAttribute) o [0];
334
335                                 ParameterStyle = a.ParameterStyle;
336                                 RoutingStyle = a.RoutingStyle;
337                                 Use = a.Use;
338                                 SoapBindingStyle = SoapBindingStyle.Document;
339                         } else {
340                                 o = Type.GetCustomAttributes (typeof (SoapRpcServiceAttribute), false);
341                                 if (o.Length == 1){
342                                         SoapRpcServiceAttribute srs = (SoapRpcServiceAttribute) o [0];
343                                         
344                                         ParameterStyle = SoapParameterStyle.Wrapped;
345                                         RoutingStyle = srs.RoutingStyle;
346                                         Use = SoapBindingUse.Encoded;
347                                         SoapBindingStyle = SoapBindingStyle.Rpc;
348                                 } else {
349                                         ParameterStyle = SoapParameterStyle.Wrapped;
350                                         RoutingStyle = SoapServiceRoutingStyle.SoapAction;
351                                         Use = SoapBindingUse.Literal;
352                                         SoapBindingStyle = SoapBindingStyle.Document;
353                                 }
354                         }
355                         
356                         if (ParameterStyle == SoapParameterStyle.Default) ParameterStyle = SoapParameterStyle.Wrapped;
357                         if (Use == SoapBindingUse.Default) Use = SoapBindingUse.Literal;
358
359                         SoapExtensions = SoapExtension.GetTypeExtensions (Type);
360                 }
361
362                 public override XmlReflectionImporter XmlImporter 
363                 {
364                         get { return xmlImporter; }
365                 }
366
367                 public override SoapReflectionImporter SoapImporter 
368                 {
369                         get { return soapImporter; }
370                 }
371                 
372                 public override string ProtocolName
373                 {
374                         get { return "Soap"; }
375                 }
376                 
377                 protected override MethodStubInfo CreateMethodStubInfo (TypeStubInfo parent, LogicalMethodInfo lmi, bool isClientProxy)
378                 {
379                         SoapMethodStubInfo res = null;
380                         object [] ats = lmi.GetCustomAttributes (typeof (SoapDocumentMethodAttribute));
381                         if (ats.Length == 0) ats = lmi.GetCustomAttributes (typeof (SoapRpcMethodAttribute));
382
383                         if (ats.Length == 0 && isClientProxy)
384                                 return null;
385                         else if (ats.Length == 0)
386                                 res = new SoapMethodStubInfo (parent, lmi, null, xmlImporter, soapImporter);
387                         else
388                                 res = new SoapMethodStubInfo (parent, lmi, ats[0], xmlImporter, soapImporter);
389                                 
390                         if (faultSerializerEncId == -1 && res.Use == SoapBindingUse.Encoded)
391                         {
392                                 SoapReflectionImporter ri = new SoapReflectionImporter ();
393                                 XmlTypeMapping tm = ri.ImportTypeMapping (typeof(Fault));
394                                 faultSerializerEncId = RegisterSerializer (tm);
395                         }
396                         else if (faultSerializerLitId == -1 && res.Use == SoapBindingUse.Literal)
397                         {
398                                 XmlReflectionImporter ri = new XmlReflectionImporter ();
399                                 XmlTypeMapping tm = ri.ImportTypeMapping (typeof(Fault));
400                                 faultSerializerLitId = RegisterSerializer (tm);
401                         }
402                         return res;
403                 }
404                 
405                 public XmlSerializer GetFaultSerializer (SoapBindingUse use)
406                 {
407                         if (use == SoapBindingUse.Literal)
408                                 return GetSerializer (faultSerializerLitId);
409                         else
410                                 return GetSerializer (faultSerializerEncId);
411                 }
412                 
413                 internal void RegisterHeaderType (Type type, SoapBindingUse use)
414                 {
415                         Hashtable serializers = header_serializers [(int)use];
416                         if (serializers == null) {
417                                 serializers = new Hashtable ();
418                                 header_serializers [(int)use] = serializers;
419                                 header_serializers_byname [(int)use] = new Hashtable ();
420                         }
421                         
422                         if (serializers.ContainsKey (type)) 
423                                 return;
424
425                         XmlTypeMapping tm;
426                         if (use == SoapBindingUse.Literal) {
427                                 XmlReflectionImporter ri = new XmlReflectionImporter ();
428                                 tm = ri.ImportTypeMapping (type, WebServiceAttribute.DefaultNamespace);
429                         }
430                         else {
431                                 SoapReflectionImporter ri = new SoapReflectionImporter ();
432                                 tm = ri.ImportTypeMapping (type, WebServiceAttribute.DefaultNamespace);
433                         }
434                         
435                         int sid = RegisterSerializer (tm);
436
437                         serializers [type] = sid;
438                         header_serializers_byname [(int)use] [new XmlQualifiedName (tm.ElementName, tm.Namespace)] = sid;
439                 }
440
441                 internal XmlSerializer GetHeaderSerializer (Type type, SoapBindingUse use)
442                 {
443                         Hashtable table = header_serializers [(int)use];
444                         if (table == null) return null;
445                                 
446                         return GetSerializer ((int) table [type]);
447                 }
448         
449                 internal XmlSerializer GetHeaderSerializer (XmlQualifiedName qname, SoapBindingUse use)
450                 {
451                         Hashtable table = header_serializers_byname [(int)use];
452                         if (table == null) return null;
453                                 
454                         return GetSerializer ((int) table [qname]);
455                 }               
456         }
457 }