SoapMessageFormatter.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  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. if (_methodCallInfo == null && (soapMessage.MethodName == "FieldSetter" || soapMessage.MethodName == "FieldGetter"))
  209. _methodCallInfo = typeof(object).GetMethod (soapMessage.MethodName, bflags);
  210. // the *out* parameters aren't serialized
  211. // have to add them here
  212. _methodCallParameters = _methodCallInfo.GetParameters();
  213. object[] args = new object[_methodCallParameters.Length];
  214. int sn = 0;
  215. for (int n=0; n<_methodCallParameters.Length; n++)
  216. {
  217. ParameterInfo paramInfo = _methodCallParameters [n];
  218. Type paramType = (paramInfo.ParameterType.IsByRef ? paramInfo.ParameterType.GetElementType() : paramInfo.ParameterType);
  219. if (paramInfo.IsOut && paramInfo.ParameterType.IsByRef) {
  220. args [n] = GetNullValue (paramType);
  221. }
  222. else{
  223. object val = soapMessage.ParamValues[sn++];
  224. if(val is IConvertible)
  225. args [n] = Convert.ChangeType (val, paramType);
  226. else
  227. args [n] = val;
  228. }
  229. }
  230. headersList.Add(new Header("__Args", args, false));
  231. Header[] headers = (Header[])headersList.ToArray(typeof(Header));
  232. // build the MethodCall from the headers
  233. MethodCall mthCall = new MethodCall(headers);
  234. return (IMessage)mthCall;
  235. }
  236. // used by the server
  237. internal object BuildSoapMessageFromMethodResponse(IMethodReturnMessage mrm, out ITransportHeaders responseHeaders)
  238. {
  239. responseHeaders = new TransportHeaders();
  240. if(mrm.Exception == null) {
  241. // *normal* function return
  242. SoapMessage soapMessage = new SoapMessage();
  243. // fill the transport headers
  244. responseHeaders["Content-Type"] = "text/xml; charset=\"utf-8\"";
  245. // build the SoapMessage
  246. ArrayList paramNames = new ArrayList();
  247. ArrayList paramValues = new ArrayList();
  248. ArrayList paramTypes = new ArrayList();
  249. soapMessage.MethodName = mrm.MethodName+"Response";
  250. Type retType = ((MethodInfo)mrm.MethodBase).ReturnType;
  251. if(retType != typeof(void)) {
  252. paramNames.Add("return");
  253. paramValues.Add(mrm.ReturnValue);
  254. if (mrm.ReturnValue != null)
  255. paramTypes.Add(mrm.ReturnValue.GetType());
  256. else
  257. paramTypes.Add(retType);
  258. }
  259. for(int i = 0; i < mrm.OutArgCount; i++){
  260. paramNames.Add(mrm.GetOutArgName(i));
  261. paramValues.Add(mrm.GetOutArg(i));
  262. if(mrm.GetOutArg(i) != null) paramTypes.Add(mrm.GetOutArg(i).GetType());
  263. }
  264. soapMessage.ParamNames = (string[]) paramNames.ToArray(typeof(string));
  265. soapMessage.ParamValues = (object[]) paramValues.ToArray(typeof(object));
  266. soapMessage.ParamTypes = (Type[]) paramTypes.ToArray(typeof(Type));
  267. soapMessage.XmlNameSpace = _xmlNamespace;
  268. soapMessage.Headers = BuildMessageHeaders (mrm);
  269. return soapMessage;
  270. }
  271. else {
  272. // an Exception was thrown while executing the function
  273. responseHeaders["__HttpStatusCode"] = "500";
  274. responseHeaders["__HttpReasonPhrase"] = "Bad Request";
  275. // fill the transport headers
  276. responseHeaders["Content-Type"] = "text/xml; charset=\"utf-8\"";
  277. ServerFault serverFault = CreateServerFault(mrm.Exception);
  278. return new SoapFault("Server", String.Format(" **** {0} - {1}", mrm.Exception.GetType().ToString(), mrm.Exception.Message), null, serverFault);
  279. }
  280. }
  281. internal SoapMessage CreateSoapMessage (bool isRequest)
  282. {
  283. if (isRequest) return new SoapMessage ();
  284. int n = 0;
  285. Type[] types = new Type [_methodCallParameters.Length + 1];
  286. if (_methodCallInfo.ReturnType != typeof(void)) {
  287. types[0] = _methodCallInfo.ReturnType;
  288. n++;
  289. }
  290. foreach(ParameterInfo paramInfo in _methodCallParameters)
  291. {
  292. if (paramInfo.ParameterType.IsByRef || paramInfo.IsOut)
  293. {
  294. Type t = paramInfo.ParameterType;
  295. if (t.IsByRef) t = t.GetElementType();
  296. types [n++] = t;
  297. }
  298. }
  299. SoapMessage sm = new SoapMessage ();
  300. sm.ParamTypes = types;
  301. return sm;
  302. }
  303. // used by the server when an exception is thrown
  304. // by the called function
  305. internal ServerFault CreateServerFault(Exception e) {
  306. // it's really strange here
  307. // a ServerFault object has a private System.Exception member called *exception*
  308. // (have a look at a MS Soap message when an exception occurs on the server)
  309. // but there is not public .ctor with an Exception as parameter...????....
  310. // (maybe an internal one). So I searched another way...
  311. ServerFault sf = (ServerFault) FormatterServices.GetUninitializedObject(typeof(ServerFault));
  312. MemberInfo[] mi = FormatterServices.GetSerializableMembers(typeof(ServerFault), new StreamingContext(StreamingContextStates.All));
  313. FieldInfo fi;
  314. object[] mv = new object[mi.Length];
  315. for(int i = 0; i < mi.Length; i++) {
  316. fi = mi[i] as FieldInfo;
  317. if(fi != null && fi.FieldType == typeof(Exception)) mv[i] = e;
  318. }
  319. sf = (ServerFault) FormatterServices.PopulateObjectMembers(sf, mi, mv);
  320. return sf;
  321. }
  322. internal void GetInfoFromMethodCallMessage (IMethodMessage mcm) {
  323. _serverType = Type.GetType(mcm.TypeName, true);
  324. _methodCallInfo = RemotingServices.GetMethodBaseFromMethodMessage (mcm) as MethodInfo;
  325. _methodCallParameters = _methodCallInfo.GetParameters();
  326. }
  327. Header[] BuildMessageHeaders (IMethodMessage msg)
  328. {
  329. ArrayList headers = new ArrayList (1);
  330. foreach (string key in msg.Properties.Keys)
  331. {
  332. switch (key) {
  333. case "__Uri":
  334. case "__MethodName":
  335. case "__TypeName":
  336. case "__Args":
  337. case "__OutArgs":
  338. case "__Return":
  339. case "__MethodSignature":
  340. case "__CallContext":
  341. continue;
  342. default:
  343. object value = msg.Properties [key];
  344. if (value != null)
  345. headers.Add (new Header (key, value, false, "http://schemas.microsoft.com/clr/soap/messageProperties"));
  346. break;
  347. }
  348. }
  349. if (RemotingServices.IsMethodOverloaded (msg))
  350. headers.Add (new Header ("__MethodSignature", msg.MethodSignature, false, "http://schemas.microsoft.com/clr/soap/messageProperties"));
  351. if (msg.LogicalCallContext != null && msg.LogicalCallContext.HasInfo)
  352. headers.Add (new Header ("__CallContext", msg.LogicalCallContext, false, "http://schemas.microsoft.com/clr/soap/messageProperties"));
  353. if (headers.Count == 0) return null;
  354. return (Header[]) headers.ToArray (typeof(Header));
  355. }
  356. object GetNullValue (Type paramType)
  357. {
  358. #if TARGET_JVM
  359. if (paramType.IsEnum)
  360. {
  361. return Activator.CreateInstance(paramType);
  362. }
  363. #endif
  364. switch (Type.GetTypeCode (paramType))
  365. {
  366. case TypeCode.Boolean: return false;
  367. case TypeCode.Byte: return (byte)0;
  368. case TypeCode.Char: return '\0';
  369. case TypeCode.Decimal: return (decimal)0;
  370. case TypeCode.Double: return (double)0;
  371. case TypeCode.Int16: return (short)0;
  372. case TypeCode.Int32: return (int)0;
  373. case TypeCode.Int64: return (long)0;
  374. case TypeCode.SByte: return (sbyte)0;
  375. case TypeCode.Single: return (float)0;
  376. case TypeCode.UInt16: return (ushort)0;
  377. case TypeCode.UInt32: return (uint)0;
  378. case TypeCode.UInt64: return (ulong)0;
  379. default:
  380. #if TARGET_JVM
  381. if (paramType.IsValueType)
  382. {
  383. return Activator.CreateInstance(paramType);
  384. }
  385. #endif
  386. return null;
  387. }
  388. }
  389. }
  390. }