add ISafeSerializationData
[mcs.git] / class / System.Web.Services / System.Web.Services.Protocols / Methods.cs
blob0821d02f5dc4608bec54bee39ec4539e28a43863
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 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 //
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 //
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using HeaderInfo = System.Web.Services.Protocols.SoapHeaderMapping;
34 using System.Reflection;
35 using System.Collections;
36 using System.Xml;
37 using System.Xml.Serialization;
38 using System.Web.Services;
39 using System.Web.Services.Description;
41 namespace System.Web.Services.Protocols {
44 // This class represents all the information we extract from a MethodInfo
45 // in the SoapHttpClientProtocol derivative stub class
47 internal class SoapMethodStubInfo : MethodStubInfo
49 internal readonly string Action;
50 internal readonly string Binding;
52 // The name/namespace of the request
53 internal readonly string RequestName;
54 internal readonly string RequestNamespace;
56 // The name/namespace of the response.
57 internal readonly string ResponseName;
58 internal readonly string ResponseNamespace;
60 internal readonly bool OneWay;
61 internal readonly SoapParameterStyle ParameterStyle;
62 internal readonly SoapBindingStyle SoapBindingStyle;
63 internal readonly SoapBindingUse Use;
65 internal readonly HeaderInfo [] Headers;
66 internal readonly HeaderInfo [] InHeaders;
67 internal readonly HeaderInfo [] OutHeaders;
68 internal readonly HeaderInfo [] FaultHeaders;
69 internal readonly SoapExtensionRuntimeConfig [] SoapExtensions;
71 internal readonly XmlMembersMapping InputMembersMapping;
72 internal readonly XmlMembersMapping OutputMembersMapping;
73 internal readonly XmlMembersMapping InputHeaderMembersMapping;
74 internal readonly XmlMembersMapping OutputHeaderMembersMapping;
75 internal readonly XmlMembersMapping FaultHeaderMembersMapping;
77 private readonly int requestSerializerId;
78 private readonly int responseSerializerId;
79 private readonly int requestHeadersSerializerId = -1;
80 private readonly int responseHeadersSerializerId = -1;
81 private readonly int faultHeadersSerializerId = -1;
83 internal XmlSerializer RequestSerializer
85 get { return TypeStub.GetSerializer (requestSerializerId); }
88 internal XmlSerializer ResponseSerializer
90 get { return TypeStub.GetSerializer (responseSerializerId); }
93 internal XmlSerializer RequestHeadersSerializer
95 get { return requestHeadersSerializerId != -1 ? TypeStub.GetSerializer (requestHeadersSerializerId) : null; }
98 internal XmlSerializer ResponseHeadersSerializer
100 get { return responseHeadersSerializerId != -1 ? TypeStub.GetSerializer (responseHeadersSerializerId) : null; }
103 internal XmlSerializer FaultHeadersSerializer
105 get { return faultHeadersSerializerId != -1 ? TypeStub.GetSerializer (faultHeadersSerializerId) : null; }
110 // Constructor
112 public SoapMethodStubInfo (TypeStubInfo typeStub, LogicalMethodInfo source, object kind, XmlReflectionImporter xmlImporter, SoapReflectionImporter soapImporter)
113 : base (typeStub, source)
115 SoapTypeStubInfo parent = (SoapTypeStubInfo) typeStub;
116 XmlElementAttribute optional_ns = null;
118 if (kind == null) {
119 Use = parent.LogicalType.BindingUse;
120 RequestName = "";
121 RequestNamespace = "";
122 ResponseName = "";
123 ResponseNamespace = "";
124 ParameterStyle = parent.ParameterStyle;
125 SoapBindingStyle = parent.SoapBindingStyle;
126 OneWay = false;
127 // disabled (see bug #332150)
128 //#if NET_2_0
129 // if (parent.Type != source.DeclaringType)
130 // Binding = source.DeclaringType.Name + parent.ProtocolName;
131 //#endif
133 else if (kind is SoapDocumentMethodAttribute){
134 SoapDocumentMethodAttribute dma = (SoapDocumentMethodAttribute) kind;
136 Use = dma.Use;
137 if (Use == SoapBindingUse.Default) {
138 if (parent.SoapBindingStyle == SoapBindingStyle.Document)
139 Use = parent.LogicalType.BindingUse;
140 else
141 Use = SoapBindingUse.Literal;
144 Action = dma.Action;
145 Binding = dma.Binding;
146 RequestName = dma.RequestElementName;
147 RequestNamespace = dma.RequestNamespace;
148 ResponseName = dma.ResponseElementName;
149 ResponseNamespace = dma.ResponseNamespace;
150 ParameterStyle = dma.ParameterStyle;
151 if (ParameterStyle == SoapParameterStyle.Default)
152 ParameterStyle = parent.ParameterStyle;
153 OneWay = dma.OneWay;
154 SoapBindingStyle = SoapBindingStyle.Document;
155 } else {
156 SoapRpcMethodAttribute rma = (SoapRpcMethodAttribute) kind;
157 Use = SoapBindingUse.Encoded; // RPC always use encoded
159 Action = rma.Action;
160 if (Action != null && Action.Length == 0)
161 Action = null;
162 Binding = rma.Binding;
164 // When using RPC, MS.NET seems to ignore RequestElementName and
165 // MessageName, and it always uses the method name
166 RequestName = source.Name;
167 ResponseName = source.Name + "Response";
168 // RequestName = rma.RequestElementName;
169 // ResponseName = rma.ResponseElementName;
170 RequestNamespace = rma.RequestNamespace;
171 ResponseNamespace = rma.ResponseNamespace;
172 ParameterStyle = SoapParameterStyle.Wrapped;
173 OneWay = rma.OneWay;
174 SoapBindingStyle = SoapBindingStyle.Rpc;
176 // For RPC calls, make all arguments be part of the empty namespace
177 optional_ns = new XmlElementAttribute ();
178 optional_ns.Namespace = "";
181 if (OneWay){
182 if (source.ReturnType != typeof (void))
183 throw new Exception ("OneWay methods should not have a return value.");
184 if (source.OutParameters.Length != 0)
185 throw new Exception ("OneWay methods should not have out/ref parameters.");
188 BindingInfo binfo = parent.GetBinding (Binding);
189 if (binfo == null) throw new InvalidOperationException ("Type '" + parent.Type + "' is missing WebServiceBinding attribute that defines a binding named '" + Binding + "'.");
191 string serviceNamespace = binfo.Namespace;
193 if (RequestNamespace == "") RequestNamespace = parent.LogicalType.GetWebServiceNamespace (serviceNamespace, Use);
194 if (ResponseNamespace == "") ResponseNamespace = parent.LogicalType.GetWebServiceNamespace (serviceNamespace, Use);
195 if (RequestName == "") RequestName = Name;
196 if (ResponseName == "") ResponseName = Name + "Response";
197 if (Action == null)
198 Action = serviceNamespace.EndsWith("/") ? (serviceNamespace + Name) : (serviceNamespace + "/" + Name);
200 bool hasWrappingElem = (ParameterStyle == SoapParameterStyle.Wrapped);
201 bool writeAccessors = (SoapBindingStyle == SoapBindingStyle.Rpc);
203 XmlReflectionMember [] in_members = BuildRequestReflectionMembers (optional_ns);
204 XmlReflectionMember [] out_members = BuildResponseReflectionMembers (optional_ns);
206 if (Use == SoapBindingUse.Literal) {
207 xmlImporter.IncludeTypes (source.CustomAttributeProvider);
208 InputMembersMapping = xmlImporter.ImportMembersMapping (RequestName, RequestNamespace, in_members, hasWrappingElem);
209 OutputMembersMapping = xmlImporter.ImportMembersMapping (ResponseName, ResponseNamespace, out_members, hasWrappingElem);
211 else {
212 soapImporter.IncludeTypes (source.CustomAttributeProvider);
213 InputMembersMapping = soapImporter.ImportMembersMapping (RequestName, RequestNamespace, in_members, hasWrappingElem, writeAccessors);
214 OutputMembersMapping = soapImporter.ImportMembersMapping (ResponseName, ResponseNamespace, out_members, hasWrappingElem, writeAccessors);
217 requestSerializerId = parent.RegisterSerializer (InputMembersMapping);
218 responseSerializerId = parent.RegisterSerializer (OutputMembersMapping);
220 object[] o = source.GetCustomAttributes (typeof (SoapHeaderAttribute));
221 ArrayList allHeaderList = new ArrayList (o.Length);
222 ArrayList inHeaderList = new ArrayList (o.Length);
223 ArrayList outHeaderList = new ArrayList (o.Length);
224 ArrayList faultHeaderList = new ArrayList ();
226 SoapHeaderDirection unknownHeaderDirections = (SoapHeaderDirection)0;
228 for (int i = 0; i < o.Length; i++) {
229 SoapHeaderAttribute att = (SoapHeaderAttribute) o[i];
230 MemberInfo[] mems = source.DeclaringType.GetMember (att.MemberName);
231 if (mems.Length == 0) throw new InvalidOperationException ("Member " + att.MemberName + " not found in class " + source.DeclaringType.FullName + ".");
233 HeaderInfo header = new HeaderInfo (mems[0], att);
234 allHeaderList.Add (header);
235 if (!header.Custom) {
236 if ((header.Direction & SoapHeaderDirection.In) != 0)
237 inHeaderList.Add (header);
238 if ((header.Direction & SoapHeaderDirection.Out) != 0)
239 outHeaderList.Add (header);
240 if ((header.Direction & SoapHeaderDirection.Fault) != 0)
241 faultHeaderList.Add (header);
242 } else
243 unknownHeaderDirections |= header.Direction;
246 Headers = (HeaderInfo[]) allHeaderList.ToArray (typeof(HeaderInfo));
248 if (inHeaderList.Count > 0 || (unknownHeaderDirections & SoapHeaderDirection.In) != 0) {
249 InHeaders = (HeaderInfo[]) inHeaderList.ToArray (typeof(HeaderInfo));
250 XmlReflectionMember[] members = BuildHeadersReflectionMembers (InHeaders);
252 if (Use == SoapBindingUse.Literal)
253 InputHeaderMembersMapping = xmlImporter.ImportMembersMapping ("", RequestNamespace, members, false);
254 else
255 InputHeaderMembersMapping = soapImporter.ImportMembersMapping ("", RequestNamespace, members, false, false);
257 requestHeadersSerializerId = parent.RegisterSerializer (InputHeaderMembersMapping);
260 if (outHeaderList.Count > 0 || (unknownHeaderDirections & SoapHeaderDirection.Out) != 0) {
261 OutHeaders = (HeaderInfo[]) outHeaderList.ToArray (typeof(HeaderInfo));
262 XmlReflectionMember[] members = BuildHeadersReflectionMembers (OutHeaders);
264 if (Use == SoapBindingUse.Literal)
265 OutputHeaderMembersMapping = xmlImporter.ImportMembersMapping ("", RequestNamespace, members, false);
266 else
267 OutputHeaderMembersMapping = soapImporter.ImportMembersMapping ("", RequestNamespace, members, false, false);
269 responseHeadersSerializerId = parent.RegisterSerializer (OutputHeaderMembersMapping);
272 if (faultHeaderList.Count > 0 || (unknownHeaderDirections & SoapHeaderDirection.Fault) != 0) {
273 FaultHeaders = (HeaderInfo[]) faultHeaderList.ToArray (typeof(HeaderInfo));
274 XmlReflectionMember[] members = BuildHeadersReflectionMembers (FaultHeaders);
276 if (Use == SoapBindingUse.Literal)
277 FaultHeaderMembersMapping = xmlImporter.ImportMembersMapping ("", RequestNamespace, members, false);
278 else
279 FaultHeaderMembersMapping = soapImporter.ImportMembersMapping ("", RequestNamespace, members, false, false);
281 faultHeadersSerializerId = parent.RegisterSerializer (FaultHeaderMembersMapping);
284 SoapExtensions = SoapExtension.GetMethodExtensions (source);
287 XmlReflectionMember [] BuildRequestReflectionMembers (XmlElementAttribute optional_ns)
289 ParameterInfo [] input = MethodInfo.InParameters;
290 XmlReflectionMember [] in_members = new XmlReflectionMember [input.Length];
292 for (int i = 0; i < input.Length; i++)
294 XmlReflectionMember m = new XmlReflectionMember ();
295 m.IsReturnValue = false;
296 m.MemberName = input [i].Name;
297 m.MemberType = input [i].ParameterType;
299 m.XmlAttributes = new XmlAttributes (input[i]);
300 m.SoapAttributes = new SoapAttributes (input[i]);
302 if (m.MemberType.IsByRef)
303 m.MemberType = m.MemberType.GetElementType ();
304 if (optional_ns != null)
305 m.XmlAttributes.XmlElements.Add (optional_ns);
306 in_members [i] = m;
308 return in_members;
311 XmlReflectionMember [] BuildResponseReflectionMembers (XmlElementAttribute optional_ns)
313 ParameterInfo [] output = MethodInfo.OutParameters;
314 bool has_return_value = !(OneWay || MethodInfo.ReturnType == typeof (void));
315 XmlReflectionMember [] out_members = new XmlReflectionMember [(has_return_value ? 1 : 0) + output.Length];
316 XmlReflectionMember m;
317 int idx = 0;
319 if (has_return_value)
321 m = new XmlReflectionMember ();
322 m.IsReturnValue = true;
323 m.MemberName = Name + "Result";
324 m.MemberType = MethodInfo.ReturnType;
326 m.XmlAttributes = new XmlAttributes (MethodInfo.ReturnTypeCustomAttributeProvider);
327 m.SoapAttributes = new SoapAttributes (MethodInfo.ReturnTypeCustomAttributeProvider);
329 if (optional_ns != null)
330 m.XmlAttributes.XmlElements.Add (optional_ns);
331 idx++;
332 out_members [0] = m;
335 for (int i = 0; i < output.Length; i++)
337 m = new XmlReflectionMember ();
338 m.IsReturnValue = false;
339 m.MemberName = output [i].Name;
340 m.MemberType = output [i].ParameterType;
341 m.XmlAttributes = new XmlAttributes (output[i]);
342 m.SoapAttributes = new SoapAttributes (output[i]);
344 if (m.MemberType.IsByRef)
345 m.MemberType = m.MemberType.GetElementType ();
346 if (optional_ns != null)
347 m.XmlAttributes.XmlElements.Add (optional_ns);
348 out_members [i + idx] = m;
350 return out_members;
353 XmlReflectionMember [] BuildHeadersReflectionMembers (HeaderInfo[] headers)
355 XmlReflectionMember [] mems = new XmlReflectionMember [headers.Length];
357 for (int n=0; n<headers.Length; n++)
359 HeaderInfo header = headers [n];
361 XmlReflectionMember m = new XmlReflectionMember ();
362 m.IsReturnValue = false;
363 m.MemberName = header.HeaderType.Name;
364 m.MemberType = header.HeaderType;
366 // MS.NET reflects header classes in a weird way. The root element
367 // name is the CLR class name unless it is specified in an XmlRootAttribute.
368 // The usual is to use the xml type name by default, but not in this case.
370 XmlAttributes ats = new XmlAttributes (header.HeaderType);
371 if (ats.XmlRoot != null) {
372 XmlElementAttribute xe = new XmlElementAttribute ();
373 xe.ElementName = ats.XmlRoot.ElementName;
374 xe.Namespace = ats.XmlRoot.Namespace;
375 m.XmlAttributes = new XmlAttributes ();
376 m.XmlAttributes.XmlElements.Add (xe);
379 mems [n] = m;
381 return mems;
384 public HeaderInfo GetHeaderInfo (Type headerType)
386 foreach (HeaderInfo headerInfo in Headers)
387 if (headerInfo.HeaderType == headerType) return headerInfo;
388 return null;
391 public XmlSerializer GetBodySerializer (SoapHeaderDirection dir, bool soap12)
393 switch (dir) {
394 case SoapHeaderDirection.In: return RequestSerializer;
395 case SoapHeaderDirection.Out: return ResponseSerializer;
396 case SoapHeaderDirection.Fault: return soap12 ? Soap12Fault.Serializer : Fault.Serializer;
397 default: return null;
401 public XmlSerializer GetHeaderSerializer (SoapHeaderDirection dir)
403 switch (dir) {
404 case SoapHeaderDirection.In: return RequestHeadersSerializer;
405 case SoapHeaderDirection.Out: return ResponseHeadersSerializer;
406 case SoapHeaderDirection.Fault: return FaultHeadersSerializer;
407 default: return null;
411 HeaderInfo[] GetHeaders (SoapHeaderDirection dir)
413 switch (dir) {
414 case SoapHeaderDirection.In: return InHeaders;
415 case SoapHeaderDirection.Out: return OutHeaders;
416 case SoapHeaderDirection.Fault: return FaultHeaders;
417 default: return null;
421 public object[] GetHeaderValueArray (SoapHeaderDirection dir, SoapHeaderCollection headers)
423 HeaderInfo[] headerInfos = GetHeaders (dir);
424 if (headerInfos == null) return null;
426 object[] hs = new object [headerInfos.Length];
428 for (int n=0; n<headers.Count; n++) {
429 SoapHeader h = headers[n];
430 Type t = h.GetType();
431 for (int i=0; i<headerInfos.Length; i++)
432 if (headerInfos [i].HeaderType == t)
433 hs [i] = h;
435 return hs;
440 // Holds the metadata loaded from the type stub, as well as
441 // the metadata for all the methods in the type
443 internal class SoapTypeStubInfo : TypeStubInfo
445 Hashtable[] header_serializers = new Hashtable [3];
446 Hashtable[] header_serializers_byname = new Hashtable [3];
447 Hashtable methods_byaction = new Hashtable ();
449 // Precomputed
450 internal SoapParameterStyle ParameterStyle;
451 internal SoapExtensionRuntimeConfig[][] SoapExtensions;
452 internal SoapBindingStyle SoapBindingStyle;
453 internal XmlReflectionImporter xmlImporter;
454 internal SoapReflectionImporter soapImporter;
456 public SoapTypeStubInfo (LogicalTypeInfo logicalTypeInfo)
457 : base (logicalTypeInfo)
459 xmlImporter = new XmlReflectionImporter ();
460 soapImporter = new SoapReflectionImporter ();
462 if (typeof (SoapHttpClientProtocol).IsAssignableFrom (Type))
464 if (Bindings.Count == 0 || ((BindingInfo)Bindings[0]).WebServiceBindingAttribute == null)
465 throw new InvalidOperationException ("WebServiceBindingAttribute is required on proxy class '" + Type + "'.");
466 if (Bindings.Count > 1)
467 throw new InvalidOperationException ("Only one WebServiceBinding attribute may be specified on type '" + Type + "'.");
470 object [] o = Type.GetCustomAttributes (typeof (SoapDocumentServiceAttribute), false);
471 if (o.Length == 1){
472 SoapDocumentServiceAttribute a = (SoapDocumentServiceAttribute) o [0];
474 ParameterStyle = a.ParameterStyle;
475 SoapBindingStyle = SoapBindingStyle.Document;
476 } else {
477 o = Type.GetCustomAttributes (typeof (SoapRpcServiceAttribute), false);
478 if (o.Length == 1){
479 SoapRpcServiceAttribute srs = (SoapRpcServiceAttribute) o [0];
481 ParameterStyle = SoapParameterStyle.Wrapped;
482 SoapBindingStyle = SoapBindingStyle.Rpc;
483 } else {
484 ParameterStyle = SoapParameterStyle.Wrapped;
485 SoapBindingStyle = SoapBindingStyle.Document;
489 if (ParameterStyle == SoapParameterStyle.Default) ParameterStyle = SoapParameterStyle.Wrapped;
491 xmlImporter.IncludeTypes (Type);
492 soapImporter.IncludeTypes (Type);
494 #if MONOTOUCH
495 SoapExtensions = new SoapExtensionRuntimeConfig [2][];
496 #else
497 SoapExtensions = SoapExtension.GetTypeExtensions (Type);
498 #endif
501 internal SoapServiceRoutingStyle RoutingStyle {
502 get { return LogicalType.RoutingStyle; }
505 public override XmlReflectionImporter XmlImporter
507 get { return xmlImporter; }
510 public override SoapReflectionImporter SoapImporter
512 get { return soapImporter; }
515 public override string ProtocolName
517 get { return "Soap"; }
520 protected override MethodStubInfo CreateMethodStubInfo (TypeStubInfo parent, LogicalMethodInfo lmi, bool isClientProxy)
522 SoapMethodStubInfo res = null;
523 object [] ats = lmi.GetCustomAttributes (typeof (SoapDocumentMethodAttribute));
524 if (ats.Length == 0) ats = lmi.GetCustomAttributes (typeof (SoapRpcMethodAttribute));
526 if (ats.Length == 0 && isClientProxy)
527 return null;
528 else if (ats.Length == 0)
529 res = new SoapMethodStubInfo (parent, lmi, null, xmlImporter, soapImporter);
530 else
531 res = new SoapMethodStubInfo (parent, lmi, ats[0], xmlImporter, soapImporter);
533 methods_byaction [res.Action] = res;
534 return res;
537 public SoapMethodStubInfo GetMethodForSoapAction (string name)
539 return (SoapMethodStubInfo) methods_byaction [name.Trim ('"',' ')];
543 internal class Soap12TypeStubInfo : SoapTypeStubInfo
545 public Soap12TypeStubInfo (LogicalTypeInfo logicalTypeInfo)
546 : base (logicalTypeInfo)
550 public override string ProtocolName
552 get { return "Soap12"; }