[System.Net] Add support for .pac proxy config scripts on mac
[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 \r
226                         if (!SoapServices.DecodeXmlNamespaceForClrTypeNamespace(soapMessage.XmlNameSpace, out typeNamespace, out assemblyName))\r
227                                 throw new RemotingException ("Could not decode SoapMessage");\r
228 \r
229                         // Note that we don't need to validate the type in\r
230                         // this place because MethodCall will do it anyway.\r
231 \r
232                         if (assemblyName == null) // corlib\r
233                                 _serverType = Type.GetType (typeNamespace, true);\r
234                         else\r
235                                 _serverType = Type.GetType (typeNamespace + ", " + assemblyName, true);\r
236 \r
237                         headersList.Add(new Header("__TypeName", _serverType.FullName, false));\r
238                         \r
239                         if (soapMessage.Headers != null) {\r
240                                 foreach (Header h in soapMessage.Headers) {\r
241                                         headersList.Add (h);\r
242                                         if (h.Name == "__MethodSignature")\r
243                                                 signature = (Type[]) h.Value;\r
244                                 }\r
245                         }\r
246                         \r
247                         _xmlNamespace = soapMessage.XmlNameSpace;\r
248                         //RemMessageType messageType;\r
249                         \r
250                         BindingFlags bflags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;\r
251                         \r
252                         if (signature == null)\r
253                                 _methodCallInfo = _serverType.GetMethod(soapMessage.MethodName, bflags); \r
254                         else\r
255                                 _methodCallInfo = _serverType.GetMethod(soapMessage.MethodName, bflags, null, signature, null);\r
256                                 \r
257                         if (_methodCallInfo == null && (soapMessage.MethodName == "FieldSetter" || soapMessage.MethodName == "FieldGetter"))\r
258                                 _methodCallInfo = typeof(object).GetMethod (soapMessage.MethodName, bflags);\r
259 \r
260                         // the *out* parameters aren't serialized\r
261                         // have to add them here\r
262                         _methodCallParameters = _methodCallInfo.GetParameters();\r
263                         object[] args = new object[_methodCallParameters.Length];\r
264                         int sn = 0;\r
265                         for (int n=0; n<_methodCallParameters.Length; n++)\r
266                         {\r
267                                 ParameterInfo paramInfo = _methodCallParameters [n];\r
268                                 Type paramType = (paramInfo.ParameterType.IsByRef ? paramInfo.ParameterType.GetElementType() : paramInfo.ParameterType);\r
269 \r
270                                 if (paramInfo.IsOut && paramInfo.ParameterType.IsByRef) {\r
271                                         args [n] = GetNullValue (paramType);\r
272                                 }\r
273                                 else{\r
274                                         object val = soapMessage.ParamValues[sn++];\r
275                                         if(val is IConvertible) \r
276                                                 args [n] = Convert.ChangeType (val, paramType);\r
277                                         else\r
278                                                 args [n] = val;\r
279                                 }\r
280                         }\r
281                         \r
282                         headersList.Add(new Header("__Args", args, false));\r
283                                                 \r
284                         Header[] headers = (Header[])headersList.ToArray(typeof(Header));\r
285 \r
286                         // build the MethodCall from the headers\r
287                         MethodCall mthCall = new MethodCall(headers);\r
288                         return (IMessage)mthCall;\r
289                 }\r
290                 \r
291                 // used by the server\r
292                 internal object BuildSoapMessageFromMethodResponse(IMethodReturnMessage mrm, out ITransportHeaders responseHeaders)\r
293                 {\r
294                         responseHeaders = new TransportHeaders();\r
295 \r
296                         if(mrm.Exception == null) {\r
297                                 // *normal* function return\r
298                                 \r
299                                 SoapMessage soapMessage = new SoapMessage();\r
300                                 \r
301                                 // fill the transport headers\r
302                                 responseHeaders["Content-Type"] = "text/xml; charset=\"utf-8\"";\r
303 \r
304                                 // build the SoapMessage\r
305                                 ArrayList paramNames = new ArrayList();\r
306                                 ArrayList paramValues = new ArrayList();\r
307                                 ArrayList paramTypes = new ArrayList();\r
308                                 soapMessage.MethodName = mrm.MethodName+"Response";\r
309                                 \r
310                                 Type retType = ((MethodInfo)mrm.MethodBase).ReturnType;\r
311                                 \r
312                                 if(retType != typeof(void)) {\r
313                                         paramNames.Add("return");\r
314                                         paramValues.Add(mrm.ReturnValue);\r
315                                         if (mrm.ReturnValue != null)\r
316                                                 paramTypes.Add(mrm.ReturnValue.GetType());\r
317                                         else\r
318                                                 paramTypes.Add(retType);\r
319                                 }\r
320                                 \r
321                                 for(int i = 0; i < mrm.OutArgCount; i++){\r
322                                         paramNames.Add(mrm.GetOutArgName(i));\r
323                                         paramValues.Add(mrm.GetOutArg(i));\r
324                                         if(mrm.GetOutArg(i) != null) paramTypes.Add(mrm.GetOutArg(i).GetType());\r
325                                 }\r
326                                 soapMessage.ParamNames = (string[]) paramNames.ToArray(typeof(string));\r
327                                 soapMessage.ParamValues = (object[]) paramValues.ToArray(typeof(object));\r
328                                 soapMessage.ParamTypes = (Type[]) paramTypes.ToArray(typeof(Type));\r
329                                 soapMessage.XmlNameSpace = _xmlNamespace;\r
330                                 soapMessage.Headers = BuildMessageHeaders (mrm);\r
331                                 return soapMessage;\r
332                         }\r
333                         else {\r
334                                 // an Exception was thrown while executing the function\r
335                                 responseHeaders["__HttpStatusCode"] = "500";\r
336                                 responseHeaders["__HttpReasonPhrase"] = "Bad Request";\r
337                                 // fill the transport headers\r
338                                 responseHeaders["Content-Type"] = "text/xml; charset=\"utf-8\"";\r
339                                 ServerFault serverFault = CreateServerFault(mrm.Exception);\r
340                                 return new SoapFault("Server", String.Format(" **** {0} - {1}", mrm.Exception.GetType().ToString(), mrm.Exception.Message), null, serverFault);\r
341                         }\r
342                 }\r
343                 \r
344                 internal SoapMessage CreateSoapMessage (bool isRequest)\r
345                 {\r
346                         if (isRequest) return new SoapMessage ();\r
347                         \r
348                         int n = 0;\r
349                         Type[] types = new Type [_methodCallParameters.Length + 1];\r
350                         \r
351                         if (_methodCallInfo.ReturnType != typeof(void)) {\r
352                                 types[0] = _methodCallInfo.ReturnType;\r
353                                 n++;\r
354                         }\r
355                                 \r
356                         foreach(ParameterInfo paramInfo in _methodCallParameters)\r
357                         {\r
358                                 if (paramInfo.ParameterType.IsByRef || paramInfo.IsOut)\r
359                                 {\r
360                                         Type t = paramInfo.ParameterType;\r
361                                         if (t.IsByRef) t = t.GetElementType();\r
362                                         types [n++] = t;\r
363                                 }\r
364                         }\r
365                         SoapMessage sm = new SoapMessage ();\r
366                         sm.ParamTypes = types;\r
367                         return sm;\r
368                 }\r
369                 \r
370                 // used by the server when an exception is thrown\r
371                 // by the called function\r
372                 internal ServerFault CreateServerFault(Exception e) {\r
373                         // it's really strange here\r
374                         // a ServerFault object has a private System.Exception member called *exception*\r
375                         // (have a look at a MS Soap message when an exception occurs on the server)\r
376                         // but there is not public .ctor with an Exception as parameter...????....\r
377                         // (maybe an internal one). So I searched another way...\r
378                         ServerFault sf = (ServerFault) FormatterServices.GetUninitializedObject(typeof(ServerFault));\r
379                         MemberInfo[] mi = FormatterServices.GetSerializableMembers(typeof(ServerFault), new StreamingContext(StreamingContextStates.All));\r
380                         \r
381                         FieldInfo fi;\r
382                         object[] mv = new object[mi.Length];\r
383                         for(int i = 0; i < mi.Length; i++) {\r
384                                 fi = mi[i] as FieldInfo;\r
385                                 if(fi != null && fi.FieldType == typeof(Exception)) mv[i] = e;\r
386                         }\r
387                         sf = (ServerFault) FormatterServices.PopulateObjectMembers(sf, mi, mv);\r
388                         \r
389                         return sf;\r
390                 }\r
391 \r
392                 internal void GetInfoFromMethodCallMessage (IMethodMessage mcm) {\r
393                         _serverType = Type.GetType(mcm.TypeName, true);\r
394                         _methodCallInfo = RemotingServices.GetMethodBaseFromMethodMessage (mcm) as MethodInfo;\r
395                         _methodCallParameters = _methodCallInfo.GetParameters();\r
396                 }       \r
397                 \r
398                 Header[] BuildMessageHeaders (IMethodMessage msg)\r
399                 {\r
400                         ArrayList headers = new ArrayList (1);\r
401                         foreach (string key in msg.Properties.Keys) \r
402                         {\r
403                                 switch (key) {\r
404                                         case "__Uri":\r
405                                         case "__MethodName":\r
406                                         case "__TypeName":\r
407                                         case "__Args":\r
408                                         case "__OutArgs":\r
409                                         case "__Return":\r
410                                         case "__MethodSignature":\r
411                                         case "__CallContext":\r
412                                                 continue;\r
413         \r
414                                         default:\r
415                                                 object value = msg.Properties [key];\r
416                                                 if (value != null)\r
417                                                         headers.Add (new Header (key, value, false, "http://schemas.microsoft.com/clr/soap/messageProperties"));\r
418                                                 break;\r
419                                 }\r
420                         }\r
421                         \r
422                         if (RemotingServices.IsMethodOverloaded (msg))\r
423                                 headers.Add (new Header ("__MethodSignature", msg.MethodSignature, false, "http://schemas.microsoft.com/clr/soap/messageProperties"));\r
424                         \r
425                         if (msg.LogicalCallContext != null && msg.LogicalCallContext.HasInfo)\r
426                                 headers.Add (new Header ("__CallContext", msg.LogicalCallContext, false, "http://schemas.microsoft.com/clr/soap/messageProperties"));\r
427                         \r
428                         if (headers.Count == 0) return null;\r
429                         return (Header[]) headers.ToArray (typeof(Header));\r
430                 }\r
431                 \r
432                 object GetNullValue (Type paramType)\r
433                 {\r
434 #if TARGET_JVM                  \r
435                         if (paramType.IsEnum)\r
436                         {\r
437                                 return Activator.CreateInstance(paramType);\r
438                         }\r
439 #endif\r
440                         switch (Type.GetTypeCode (paramType))\r
441                         {\r
442                                 case TypeCode.Boolean: return false;\r
443                                 case TypeCode.Byte: return (byte)0;\r
444                                 case TypeCode.Char: return '\0';\r
445                                 case TypeCode.Decimal: return (decimal)0;\r
446                                 case TypeCode.Double: return (double)0;\r
447                                 case TypeCode.Int16: return (short)0;\r
448                                 case TypeCode.Int32: return (int)0;\r
449                                 case TypeCode.Int64: return (long)0;\r
450                                 case TypeCode.SByte: return (sbyte)0;\r
451                                 case TypeCode.Single: return (float)0;\r
452                                 case TypeCode.UInt16: return (ushort)0;\r
453                                 case TypeCode.UInt32: return (uint)0;\r
454                                 case TypeCode.UInt64: return (ulong)0;\r
455                                 default: \r
456 #if TARGET_JVM                  \r
457                                         if (paramType.IsValueType)\r
458                                         {\r
459                                                 return Activator.CreateInstance(paramType);\r
460                                         }\r
461 #endif                                  \r
462                                         return null;\r
463                         }\r
464                 }\r
465         }\r
466 }\r