* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / class / System.Runtime.Remoting / System.Runtime.Remoting.Channels / SoapMessageFormatter.cs
1 // created on 03/04/2003 at 14:09\r
2 // \r
3 // System.Runtime.Remoting.Channels.SoapMessageFormatter\r
4 //\r
5 // Author:      Jean-Marc Andre (jean-marc.andre@polymtl.ca)\r
6 //\r
7 //\r
8 \r
9 //\r
10 // Permission is hereby granted, free of charge, to any person obtaining\r
11 // a copy of this software and associated documentation files (the\r
12 // "Software"), to deal in the Software without restriction, including\r
13 // without limitation the rights to use, copy, modify, merge, publish,\r
14 // distribute, sublicense, and/or sell copies of the Software, and to\r
15 // permit persons to whom the Software is furnished to do so, subject to\r
16 // the following conditions:\r
17 // \r
18 // The above copyright notice and this permission notice shall be\r
19 // included in all copies or substantial portions of the Software.\r
20 // \r
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
28 //\r
29 \r
30 using System;\r
31 using System.Collections;\r
32 using System.Reflection;\r
33 using System.Runtime.Remoting.Messaging;\r
34 using System.Runtime.Serialization;\r
35 using System.Runtime.Serialization.Formatters;\r
36 \r
37 \r
38 \r
39 namespace System.Runtime.Remoting.Channels {\r
40         enum RemMessageType {\r
41                 MethodCall, MethodResponse, ServerFault, NotRecognize\r
42         }\r
43         \r
44         internal class SoapMessageFormatter {\r
45                 private static FieldInfo _serverFaultExceptionField;\r
46                 private Type _serverType;\r
47                 private MethodInfo _methodCallInfo;\r
48                 private ParameterInfo[] _methodCallParameters;\r
49                 private string _xmlNamespace;\r
50                 \r
51                 static SoapMessageFormatter() {\r
52                         // Get the ServerFault exception field FieldInfo that\r
53                         // will be used later if an exception occurs on the server\r
54                         MemberInfo[] mi = FormatterServices.GetSerializableMembers(typeof(ServerFault), new StreamingContext(StreamingContextStates.All));\r
55                         FieldInfo fi;\r
56                         for(int i = 0; i < mi.Length; i++){\r
57                                 fi = mi[i] as FieldInfo;\r
58                                 if(fi != null && fi.FieldType == typeof(Exception)){\r
59                                         _serverFaultExceptionField = fi; \r
60                                 }\r
61                         }\r
62                         \r
63                 }\r
64                 \r
65                 internal SoapMessageFormatter() {\r
66                         \r
67                 }\r
68                 \r
69                 internal IMessage FormatFault (SoapFault fault, IMethodCallMessage mcm)\r
70                 {\r
71                         ServerFault sf = fault.Detail as ServerFault;\r
72                         Exception e = null;\r
73                         \r
74                         if (sf != null) {\r
75                                 if(_serverFaultExceptionField != null)\r
76                                         e = (Exception) _serverFaultExceptionField.GetValue(sf);\r
77 #if TARGET_JVM                          \r
78                                 if (e == null && sf.ExceptionType != null)\r
79                                 {\r
80                                         try\r
81                                         {\r
82                                                 Type te = Type.GetType(sf.ExceptionType);\r
83                                                 if (te != null)\r
84                                                 {\r
85                                                         ConstructorInfo ce = te.GetConstructor(\r
86                                                                 BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance,\r
87                                                                 null, new Type[] {typeof(string)}, null);\r
88 \r
89                                                         if (ce != null)\r
90                                                         {\r
91                                                                 e = (Exception) ce.Invoke(new object[] {sf.ExceptionMessage});\r
92                                                         }\r
93                                                         else\r
94                                                         {\r
95                                                                 e = (Exception) Activator.CreateInstance(te);\r
96                                                         }\r
97                                                 }\r
98                                         }\r
99                                         catch\r
100                                         {\r
101                                                 e = null;\r
102                                         }\r
103                                 }\r
104 #endif\r
105                         }\r
106                         if (e == null)\r
107                                 e = new RemotingException (fault.FaultString);\r
108 \r
109                         return new ReturnMessage((System.Exception)e, mcm);\r
110                 }\r
111                 \r
112                 // used by the client\r
113                 internal IMessage FormatResponse(ISoapMessage soapMsg, IMethodCallMessage mcm) \r
114                 {\r
115                         IMessage rtnMsg;\r
116                         \r
117                         if(soapMsg.MethodName == "Fault") {\r
118                                 // an exception was thrown by the server\r
119                                 Exception e = new SerializationException();\r
120                                 int i = Array.IndexOf(soapMsg.ParamNames, "detail");\r
121                                 if(_serverFaultExceptionField != null)\r
122                                         // todo: revue this 'cause it's not safe\r
123                                         e = (Exception) _serverFaultExceptionField.GetValue(\r
124                                                         soapMsg.ParamValues[i]);\r
125                                 \r
126                                 rtnMsg = new ReturnMessage((System.Exception)e, mcm);\r
127                         }\r
128                         else {\r
129                                 object rtnObject = null;\r
130                                 //RemMessageType messageType;\r
131                                 \r
132                                 // Get the output of the function if it is not *void*\r
133                                 if(_methodCallInfo.ReturnType != typeof(void)){\r
134                                         int index = Array.IndexOf(soapMsg.ParamNames, "return");\r
135                                         rtnObject = soapMsg.ParamValues[index];\r
136                                         if(rtnObject is IConvertible) \r
137                                                 rtnObject = Convert.ChangeType(\r
138                                                                 rtnObject, \r
139                                                                 _methodCallInfo.ReturnType);\r
140                                 }\r
141                                 \r
142                                 object[] outParams = new object [_methodCallParameters.Length];\r
143                                 int n=0;\r
144                                 \r
145                                 // check if there are *out* parameters\r
146                                 foreach(ParameterInfo paramInfo in _methodCallParameters) {\r
147                                         \r
148                                         if(paramInfo.ParameterType.IsByRef || paramInfo.IsOut) {\r
149                                                 int index = Array.IndexOf(soapMsg.ParamNames, paramInfo.Name);\r
150                                                 object outParam = soapMsg.ParamValues[index];\r
151                                                 if(outParam is IConvertible)\r
152                                                         outParam = Convert.ChangeType (outParam, paramInfo.ParameterType.GetElementType());\r
153                                                 outParams[n] = outParam;\r
154                                         }\r
155                                         else\r
156                                                 outParams [n] = null;\r
157                                         n++;\r
158                                 }\r
159                                 \r
160                                 Header[] headers = new Header [2 + (soapMsg.Headers != null ? soapMsg.Headers.Length : 0)];\r
161                                 headers [0] = new Header ("__Return", rtnObject);\r
162                                 headers [1] = new Header ("__OutArgs", outParams);\r
163                                 \r
164                                 if (soapMsg.Headers != null)\r
165                                         soapMsg.Headers.CopyTo (headers, 2);\r
166                                         \r
167                                 rtnMsg = new MethodResponse (headers, mcm);\r
168                         }\r
169                         return rtnMsg;\r
170                 }\r
171                 \r
172                 // used by the client\r
173                 internal SoapMessage BuildSoapMessageFromMethodCall(\r
174                                 IMethodCallMessage mcm,\r
175                                 out ITransportHeaders requestHeaders)\r
176                 {\r
177                         \r
178                         requestHeaders = new TransportHeaders();\r
179                         SoapMessage soapMsg = new SoapMessage();\r
180 \r
181                         GetInfoFromMethodCallMessage(mcm);\r
182 \r
183                         // Format the SoapMessage that will be used to create the RPC\r
184                         soapMsg.MethodName = mcm.MethodName;\r
185                         //int count = mcm.ArgCount;\r
186                         ArrayList paramNames = new ArrayList(_methodCallParameters.Length);\r
187                         ArrayList paramTypes = new ArrayList(_methodCallParameters.Length);\r
188                         ArrayList paramValues = new ArrayList(_methodCallParameters.Length);\r
189                         \r
190                         // Add the function parameters to the SoapMessage class\r
191                         foreach(ParameterInfo paramInfo in _methodCallParameters) {\r
192                                 if (!(paramInfo.IsOut && paramInfo.ParameterType.IsByRef)) {\r
193                                         Type t = paramInfo.ParameterType;\r
194                                         if (t.IsByRef) t = t.GetElementType ();\r
195                                         paramNames.Add(paramInfo.Name);\r
196                                         paramTypes.Add(t);\r
197                                         paramValues.Add(mcm.Args[paramInfo.Position]);\r
198                                 }\r
199                         }                       \r
200                         soapMsg.ParamNames = (string[]) paramNames.ToArray(typeof(string));\r
201                         soapMsg.ParamTypes = (Type[]) paramTypes.ToArray(typeof(Type));\r
202                         soapMsg.ParamValues = (object[]) paramValues.ToArray(typeof(object));\r
203                         soapMsg.XmlNameSpace = SoapServices.GetXmlNamespaceForMethodCall(_methodCallInfo);\r
204                         soapMsg.Headers = BuildMessageHeaders (mcm);\r
205 \r
206                         // Format the transport headers\r
207                         requestHeaders["Content-Type"] = "text/xml; charset=\"utf-8\"";\r
208                         requestHeaders["SOAPAction"] = "\""+\r
209                                 SoapServices.GetSoapActionFromMethodBase(_methodCallInfo)+"\""; \r
210                         requestHeaders[CommonTransportKeys.RequestUri] = mcm.Uri;\r
211                         \r
212                         return soapMsg;\r
213                         \r
214                 }\r
215                 \r
216                 // used by the server\r
217                 internal IMessage BuildMethodCallFromSoapMessage(SoapMessage soapMessage, string uri) \r
218                 {\r
219                         ArrayList headersList = new ArrayList();\r
220                         Type[] signature = null;\r
221                         \r
222                         headersList.Add(new Header("__Uri", uri));\r
223                         headersList.Add(new Header("__MethodName", soapMessage.MethodName));\r
224                         string typeNamespace, assemblyName;\r
225                         SoapServices.DecodeXmlNamespaceForClrTypeNamespace(soapMessage.XmlNameSpace, out typeNamespace, out assemblyName);\r
226 \r
227                         _serverType = RemotingServices.GetServerTypeForUri(uri);\r
228                         headersList.Add(new Header("__TypeName", _serverType.FullName, false));\r
229                         \r
230                         if (soapMessage.Headers != null) {\r
231                                 foreach (Header h in soapMessage.Headers) {\r
232                                         headersList.Add (h);\r
233                                         if (h.Name == "__MethodSignature")\r
234                                                 signature = (Type[]) h.Value;\r
235                                 }\r
236                         }\r
237                         \r
238                         _xmlNamespace = soapMessage.XmlNameSpace;\r
239                         //RemMessageType messageType;\r
240                         \r
241                         BindingFlags bflags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;\r
242                         \r
243                         if (signature == null)\r
244                                 _methodCallInfo = _serverType.GetMethod(soapMessage.MethodName, bflags); \r
245                         else\r
246                                 _methodCallInfo = _serverType.GetMethod(soapMessage.MethodName, bflags, null, signature, null); \r
247 \r
248                         // the *out* parameters aren't serialized\r
249                         // have to add them here\r
250                         _methodCallParameters = _methodCallInfo.GetParameters();\r
251                         object[] args = new object[_methodCallParameters.Length];\r
252                         int sn = 0;\r
253                         for (int n=0; n<_methodCallParameters.Length; n++)\r
254                         {\r
255                                 ParameterInfo paramInfo = _methodCallParameters [n];\r
256                                 Type paramType = (paramInfo.ParameterType.IsByRef ? paramInfo.ParameterType.GetElementType() : paramInfo.ParameterType);\r
257 \r
258                                 if (paramInfo.IsOut && paramInfo.ParameterType.IsByRef) {\r
259                                         args [n] = GetNullValue (paramType);\r
260                                 }\r
261                                 else{\r
262                                         object val = soapMessage.ParamValues[sn++];\r
263                                         if(val is IConvertible) \r
264                                                 args [n] = Convert.ChangeType (val, paramType);\r
265                                         else\r
266                                                 args [n] = val;\r
267                                 }\r
268                         }\r
269                         \r
270                         headersList.Add(new Header("__Args", args, false));\r
271                                                 \r
272                         Header[] headers = (Header[])headersList.ToArray(typeof(Header));\r
273 \r
274                         // build the MethodCall from the headers\r
275                         MethodCall mthCall = new MethodCall(headers);\r
276                         return (IMessage)mthCall;\r
277                 }\r
278                 \r
279                 // used by the server\r
280                 internal object BuildSoapMessageFromMethodResponse(IMethodReturnMessage mrm, out ITransportHeaders responseHeaders)\r
281                 {\r
282                         responseHeaders = new TransportHeaders();\r
283 \r
284                         if(mrm.Exception == null) {\r
285                                 // *normal* function return\r
286                                 \r
287                                 SoapMessage soapMessage = new SoapMessage();\r
288                                 \r
289                                 // fill the transport headers\r
290                                 responseHeaders["Content-Type"] = "text/xml; charset=\"utf-8\"";\r
291 \r
292                                 // build the SoapMessage\r
293                                 ArrayList paramNames = new ArrayList();\r
294                                 ArrayList paramValues = new ArrayList();\r
295                                 ArrayList paramTypes = new ArrayList();\r
296                                 soapMessage.MethodName = mrm.MethodName+"Response";\r
297                                 \r
298                                 Type retType = ((MethodInfo)mrm.MethodBase).ReturnType;\r
299                                 \r
300                                 if(retType != typeof(void)) {\r
301                                         paramNames.Add("return");\r
302                                         paramValues.Add(mrm.ReturnValue);\r
303                                         if (mrm.ReturnValue != null)\r
304                                                 paramTypes.Add(mrm.ReturnValue.GetType());\r
305                                         else\r
306                                                 paramTypes.Add(retType);\r
307                                 }\r
308                                 \r
309                                 for(int i = 0; i < mrm.OutArgCount; i++){\r
310                                         paramNames.Add(mrm.GetOutArgName(i));\r
311                                         paramValues.Add(mrm.GetOutArg(i));\r
312                                         if(mrm.GetOutArg(i) != null) paramTypes.Add(mrm.GetOutArg(i).GetType());\r
313                                 }\r
314                                 soapMessage.ParamNames = (string[]) paramNames.ToArray(typeof(string));\r
315                                 soapMessage.ParamValues = (object[]) paramValues.ToArray(typeof(object));\r
316                                 soapMessage.ParamTypes = (Type[]) paramTypes.ToArray(typeof(Type));\r
317                                 soapMessage.XmlNameSpace = _xmlNamespace;\r
318                                 soapMessage.Headers = BuildMessageHeaders (mrm);\r
319                                 return soapMessage;\r
320                         }\r
321                         else {\r
322                                 // an Exception was thrown while executing the function\r
323                                 responseHeaders["__HttpStatusCode"] = "500";\r
324                                 responseHeaders["__HttpReasonPhrase"] = "Bad Request";\r
325                                 // fill the transport headers\r
326                                 responseHeaders["Content-Type"] = "text/xml; charset=\"utf-8\"";\r
327                                 ServerFault serverFault = CreateServerFault(mrm.Exception);\r
328                                 return new SoapFault("Server", String.Format(" **** {0} - {1}", mrm.Exception.GetType().ToString(), mrm.Exception.Message), null, serverFault);\r
329                         }\r
330                 }\r
331                 \r
332                 internal SoapMessage CreateSoapMessage (bool isRequest)\r
333                 {\r
334                         if (isRequest) return new SoapMessage ();\r
335                         \r
336                         int n = 0;\r
337                         Type[] types = new Type [_methodCallParameters.Length + 1];\r
338                         \r
339                         if (_methodCallInfo.ReturnType != typeof(void)) {\r
340                                 types[0] = _methodCallInfo.ReturnType;\r
341                                 n++;\r
342                         }\r
343                                 \r
344                         foreach(ParameterInfo paramInfo in _methodCallParameters)\r
345                         {\r
346                                 if (paramInfo.ParameterType.IsByRef || paramInfo.IsOut)\r
347                                 {\r
348                                         Type t = paramInfo.ParameterType;\r
349                                         if (t.IsByRef) t = t.GetElementType();\r
350                                         types [n++] = t;\r
351                                 }\r
352                         }\r
353                         SoapMessage sm = new SoapMessage ();\r
354                         sm.ParamTypes = types;\r
355                         return sm;\r
356                 }\r
357                 \r
358                 // used by the server when an exception is thrown\r
359                 // by the called function\r
360                 internal ServerFault CreateServerFault(Exception e) {\r
361                         // it's really strange here\r
362                         // a ServerFault object has a private System.Exception member called *exception*\r
363                         // (have a look at a MS Soap message when an exception occurs on the server)\r
364                         // but there is not public .ctor with an Exception as parameter...????....\r
365                         // (maybe an internal one). So I searched another way...\r
366                         ServerFault sf = (ServerFault) FormatterServices.GetUninitializedObject(typeof(ServerFault));\r
367                         MemberInfo[] mi = FormatterServices.GetSerializableMembers(typeof(ServerFault), new StreamingContext(StreamingContextStates.All));\r
368                         \r
369                         FieldInfo fi;\r
370                         object[] mv = new object[mi.Length];\r
371                         for(int i = 0; i < mi.Length; i++) {\r
372                                 fi = mi[i] as FieldInfo;\r
373                                 if(fi != null && fi.FieldType == typeof(Exception)) mv[i] = e;\r
374                         }\r
375                         sf = (ServerFault) FormatterServices.PopulateObjectMembers(sf, mi, mv);\r
376                         \r
377                         return sf;\r
378                 }\r
379 \r
380                 internal void GetInfoFromMethodCallMessage(IMethodCallMessage mcm) {\r
381                         _serverType = Type.GetType(mcm.TypeName, true);\r
382                         \r
383                         if (mcm.MethodSignature != null) \r
384                                 _methodCallInfo = _serverType.GetMethod(mcm.MethodName, \r
385                                                                                                                 BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, \r
386                                                                                                                 null, (Type []) mcm.MethodSignature, null);\r
387                         else\r
388                                 _methodCallInfo = _serverType.GetMethod(mcm.MethodName, \r
389                                                                                                                 BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);\r
390 \r
391                         _methodCallParameters = _methodCallInfo.GetParameters();\r
392                 }       \r
393                 \r
394                 Header[] BuildMessageHeaders (IMethodMessage msg)\r
395                 {\r
396                         ArrayList headers = new ArrayList (1);\r
397                         foreach (string key in msg.Properties.Keys) \r
398                         {\r
399                                 switch (key) {\r
400                                         case "__Uri":\r
401                                         case "__MethodName":\r
402                                         case "__TypeName":\r
403                                         case "__Args":\r
404                                         case "__OutArgs":\r
405                                         case "__Return":\r
406                                         case "__MethodSignature":\r
407                                         case "__CallContext":\r
408                                                 continue;\r
409         \r
410                                         default:\r
411                                                 object value = msg.Properties [key];\r
412                                                 if (value != null)\r
413                                                         headers.Add (new Header (key, value, false, "http://schemas.microsoft.com/clr/soap/messageProperties"));\r
414                                                 break;\r
415                                 }\r
416                         }\r
417                         \r
418                         if (RemotingServices.IsMethodOverloaded (msg))\r
419                                 headers.Add (new Header ("__MethodSignature", msg.MethodSignature, false, "http://schemas.microsoft.com/clr/soap/messageProperties"));\r
420                         \r
421                         if (msg.LogicalCallContext != null && msg.LogicalCallContext.HasInfo)\r
422                                 headers.Add (new Header ("__CallContext", msg.LogicalCallContext, false, "http://schemas.microsoft.com/clr/soap/messageProperties"));\r
423                         \r
424                         if (headers.Count == 0) return null;\r
425                         return (Header[]) headers.ToArray (typeof(Header));\r
426                 }\r
427                 \r
428                 object GetNullValue (Type paramType)\r
429                 {\r
430 #if TARGET_JVM                  \r
431                         if (paramType.IsEnum)\r
432                         {\r
433                                 return Activator.CreateInstance(paramType);\r
434                         }\r
435 #endif\r
436                         switch (Type.GetTypeCode (paramType))\r
437                         {\r
438                                 case TypeCode.Boolean: return false;\r
439                                 case TypeCode.Byte: return (byte)0;\r
440                                 case TypeCode.Char: return '\0';\r
441                                 case TypeCode.Decimal: return (decimal)0;\r
442                                 case TypeCode.Double: return (double)0;\r
443                                 case TypeCode.Int16: return (short)0;\r
444                                 case TypeCode.Int32: return (int)0;\r
445                                 case TypeCode.Int64: return (long)0;\r
446                                 case TypeCode.SByte: return (sbyte)0;\r
447                                 case TypeCode.Single: return (float)0;\r
448                                 case TypeCode.UInt16: return (ushort)0;\r
449                                 case TypeCode.UInt32: return (uint)0;\r
450                                 case TypeCode.UInt64: return (ulong)0;\r
451                                 default: \r
452 #if TARGET_JVM                  \r
453                                         if (paramType.IsValueType)\r
454                                         {\r
455                                                 return Activator.CreateInstance(paramType);\r
456                                         }\r
457 #endif                                  \r
458                                         return null;\r
459                         }\r
460                 }\r
461         }\r
462 }\r