SoapMessageFormatter.cs 16 KB

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