SoapMessageFormatter.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  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. }
  66. if (e == null)
  67. e = new RemotingException (fault.FaultString);
  68. return new ReturnMessage((System.Exception)e, mcm);
  69. }
  70. // used by the client
  71. internal IMessage FormatResponse(ISoapMessage soapMsg, IMethodCallMessage mcm)
  72. {
  73. IMessage rtnMsg;
  74. if(soapMsg.MethodName == "Fault") {
  75. // an exception was thrown by the server
  76. Exception e = new SerializationException();
  77. int i = Array.IndexOf(soapMsg.ParamNames, "detail");
  78. if(_serverFaultExceptionField != null)
  79. // todo: revue this 'cause it's not safe
  80. e = (Exception) _serverFaultExceptionField.GetValue(
  81. soapMsg.ParamValues[i]);
  82. rtnMsg = new ReturnMessage((System.Exception)e, mcm);
  83. }
  84. else {
  85. object rtnObject = null;
  86. //RemMessageType messageType;
  87. // Get the output of the function if it is not *void*
  88. if(_methodCallInfo.ReturnType != typeof(void)){
  89. int index = Array.IndexOf(soapMsg.ParamNames, "return");
  90. rtnObject = soapMsg.ParamValues[index];
  91. if(rtnObject is IConvertible)
  92. rtnObject = Convert.ChangeType(
  93. rtnObject,
  94. _methodCallInfo.ReturnType);
  95. }
  96. object[] outParams = new object [_methodCallParameters.Length];
  97. int n=0;
  98. // check if there are *out* parameters
  99. foreach(ParameterInfo paramInfo in _methodCallParameters) {
  100. if(paramInfo.ParameterType.IsByRef || paramInfo.IsOut) {
  101. int index = Array.IndexOf(soapMsg.ParamNames, paramInfo.Name);
  102. object outParam = soapMsg.ParamValues[index];
  103. if(outParam is IConvertible)
  104. outParam = Convert.ChangeType (outParam, paramInfo.ParameterType.GetElementType());
  105. outParams[n] = outParam;
  106. }
  107. else
  108. outParams [n] = null;
  109. n++;
  110. }
  111. Header[] headers = new Header [2 + (soapMsg.Headers != null ? soapMsg.Headers.Length : 0)];
  112. headers [0] = new Header ("__Return", rtnObject);
  113. headers [1] = new Header ("__OutArgs", outParams);
  114. if (soapMsg.Headers != null)
  115. soapMsg.Headers.CopyTo (headers, 2);
  116. rtnMsg = new MethodResponse (headers, mcm);
  117. }
  118. return rtnMsg;
  119. }
  120. // used by the client
  121. internal SoapMessage BuildSoapMessageFromMethodCall(
  122. IMethodCallMessage mcm,
  123. out ITransportHeaders requestHeaders)
  124. {
  125. requestHeaders = new TransportHeaders();
  126. SoapMessage soapMsg = new SoapMessage();
  127. GetInfoFromMethodCallMessage(mcm);
  128. // Format the SoapMessage that will be used to create the RPC
  129. soapMsg.MethodName = mcm.MethodName;
  130. //int count = mcm.ArgCount;
  131. ArrayList paramNames = new ArrayList(_methodCallParameters.Length);
  132. ArrayList paramTypes = new ArrayList(_methodCallParameters.Length);
  133. ArrayList paramValues = new ArrayList(_methodCallParameters.Length);
  134. // Add the function parameters to the SoapMessage class
  135. foreach(ParameterInfo paramInfo in _methodCallParameters) {
  136. if (!(paramInfo.IsOut && paramInfo.ParameterType.IsByRef)) {
  137. Type t = paramInfo.ParameterType;
  138. if (t.IsByRef) t = t.GetElementType ();
  139. paramNames.Add(paramInfo.Name);
  140. paramTypes.Add(t);
  141. paramValues.Add(mcm.Args[paramInfo.Position]);
  142. }
  143. }
  144. soapMsg.ParamNames = (string[]) paramNames.ToArray(typeof(string));
  145. soapMsg.ParamTypes = (Type[]) paramTypes.ToArray(typeof(Type));
  146. soapMsg.ParamValues = (object[]) paramValues.ToArray(typeof(object));
  147. soapMsg.XmlNameSpace = SoapServices.GetXmlNamespaceForMethodCall(_methodCallInfo);
  148. soapMsg.Headers = BuildMessageHeaders (mcm);
  149. // Format the transport headers
  150. requestHeaders["Content-Type"] = "text/xml; charset=\"utf-8\"";
  151. requestHeaders["SOAPAction"] = "\""+
  152. SoapServices.GetSoapActionFromMethodBase(_methodCallInfo)+"\"";
  153. requestHeaders[CommonTransportKeys.RequestUri] = mcm.Uri;
  154. return soapMsg;
  155. }
  156. // used by the server
  157. internal IMessage BuildMethodCallFromSoapMessage(SoapMessage soapMessage, string uri)
  158. {
  159. ArrayList headersList = new ArrayList();
  160. Type[] signature = null;
  161. headersList.Add(new Header("__Uri", uri));
  162. headersList.Add(new Header("__MethodName", soapMessage.MethodName));
  163. string typeNamespace, assemblyName;
  164. SoapServices.DecodeXmlNamespaceForClrTypeNamespace(soapMessage.XmlNameSpace, out typeNamespace, out assemblyName);
  165. _serverType = RemotingServices.GetServerTypeForUri(uri);
  166. headersList.Add(new Header("__TypeName", _serverType.FullName, false));
  167. if (soapMessage.Headers != null) {
  168. foreach (Header h in soapMessage.Headers) {
  169. headersList.Add (h);
  170. if (h.Name == "__MethodSignature")
  171. signature = (Type[]) h.Value;
  172. }
  173. }
  174. _xmlNamespace = soapMessage.XmlNameSpace;
  175. //RemMessageType messageType;
  176. BindingFlags bflags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
  177. if (signature == null)
  178. _methodCallInfo = _serverType.GetMethod(soapMessage.MethodName, bflags);
  179. else
  180. _methodCallInfo = _serverType.GetMethod(soapMessage.MethodName, bflags, null, signature, null);
  181. // the *out* parameters aren't serialized
  182. // have to add them here
  183. _methodCallParameters = _methodCallInfo.GetParameters();
  184. object[] args = new object[_methodCallParameters.Length];
  185. int sn = 0;
  186. for (int n=0; n<_methodCallParameters.Length; n++)
  187. {
  188. ParameterInfo paramInfo = _methodCallParameters [n];
  189. Type paramType = (paramInfo.ParameterType.IsByRef ? paramInfo.ParameterType.GetElementType() : paramInfo.ParameterType);
  190. if (paramInfo.IsOut && paramInfo.ParameterType.IsByRef) {
  191. args [n] = GetNullValue (paramType);
  192. }
  193. else{
  194. object val = soapMessage.ParamValues[sn++];
  195. if(val is IConvertible)
  196. args [n] = Convert.ChangeType (val, paramType);
  197. else
  198. args [n] = val;
  199. }
  200. }
  201. headersList.Add(new Header("__Args", args, false));
  202. Header[] headers = (Header[])headersList.ToArray(typeof(Header));
  203. // build the MethodCall from the headers
  204. MethodCall mthCall = new MethodCall(headers);
  205. return (IMessage)mthCall;
  206. }
  207. // used by the server
  208. internal object BuildSoapMessageFromMethodResponse(IMethodReturnMessage mrm, out ITransportHeaders responseHeaders)
  209. {
  210. responseHeaders = new TransportHeaders();
  211. if(mrm.Exception == null) {
  212. // *normal* function return
  213. SoapMessage soapMessage = new SoapMessage();
  214. // fill the transport headers
  215. responseHeaders["Content-Type"] = "text/xml; charset=\"utf-8\"";
  216. // build the SoapMessage
  217. ArrayList paramNames = new ArrayList();
  218. ArrayList paramValues = new ArrayList();
  219. ArrayList paramTypes = new ArrayList();
  220. soapMessage.MethodName = mrm.MethodName+"Response";
  221. Type retType = ((MethodInfo)mrm.MethodBase).ReturnType;
  222. if(retType != typeof(void)) {
  223. paramNames.Add("return");
  224. paramValues.Add(mrm.ReturnValue);
  225. if (mrm.ReturnValue != null)
  226. paramTypes.Add(mrm.ReturnValue.GetType());
  227. else
  228. paramTypes.Add(retType);
  229. }
  230. for(int i = 0; i < mrm.OutArgCount; i++){
  231. paramNames.Add(mrm.GetOutArgName(i));
  232. paramValues.Add(mrm.GetOutArg(i));
  233. if(mrm.GetOutArg(i) != null) paramTypes.Add(mrm.GetOutArg(i).GetType());
  234. }
  235. soapMessage.ParamNames = (string[]) paramNames.ToArray(typeof(string));
  236. soapMessage.ParamValues = (object[]) paramValues.ToArray(typeof(object));
  237. soapMessage.ParamTypes = (Type[]) paramTypes.ToArray(typeof(Type));
  238. soapMessage.XmlNameSpace = _xmlNamespace;
  239. soapMessage.Headers = BuildMessageHeaders (mrm);
  240. return soapMessage;
  241. }
  242. else {
  243. // an Exception was thrown while executing the function
  244. responseHeaders["__HttpStatusCode"] = "400";
  245. responseHeaders["__HttpReasonPhrase"] = "Bad Request";
  246. // fill the transport headers
  247. responseHeaders["Content-Type"] = "text/xml; charset=\"utf-8\"";
  248. ServerFault serverFault = CreateServerFault(mrm.Exception);
  249. return new SoapFault("Server", String.Format(" **** {0} - {1}", mrm.Exception.GetType().ToString(), mrm.Exception.Message), null, serverFault);
  250. }
  251. }
  252. internal SoapMessage CreateSoapMessage (bool isRequest)
  253. {
  254. if (isRequest) return new SoapMessage ();
  255. int n = 0;
  256. Type[] types = new Type [_methodCallParameters.Length + 1];
  257. if (_methodCallInfo.ReturnType != typeof(void)) {
  258. types[0] = _methodCallInfo.ReturnType;
  259. n++;
  260. }
  261. foreach(ParameterInfo paramInfo in _methodCallParameters)
  262. {
  263. if (paramInfo.ParameterType.IsByRef || paramInfo.IsOut)
  264. {
  265. Type t = paramInfo.ParameterType;
  266. if (t.IsByRef) t = t.GetElementType();
  267. types [n++] = t;
  268. }
  269. }
  270. SoapMessage sm = new SoapMessage ();
  271. sm.ParamTypes = types;
  272. return sm;
  273. }
  274. // used by the server when an exception is thrown
  275. // by the called function
  276. internal ServerFault CreateServerFault(Exception e) {
  277. // it's really strange here
  278. // a ServerFault object has a private System.Exception member called *exception*
  279. // (have a look at a MS Soap message when an exception occurs on the server)
  280. // but there is not public .ctor with an Exception as parameter...????....
  281. // (maybe an internal one). So I searched another way...
  282. ServerFault sf = (ServerFault) FormatterServices.GetUninitializedObject(typeof(ServerFault));
  283. MemberInfo[] mi = FormatterServices.GetSerializableMembers(typeof(ServerFault), new StreamingContext(StreamingContextStates.All));
  284. FieldInfo fi;
  285. object[] mv = new object[mi.Length];
  286. for(int i = 0; i < mi.Length; i++) {
  287. fi = mi[i] as FieldInfo;
  288. if(fi != null && fi.FieldType == typeof(Exception)) mv[i] = e;
  289. }
  290. sf = (ServerFault) FormatterServices.PopulateObjectMembers(sf, mi, mv);
  291. return sf;
  292. }
  293. internal void GetInfoFromMethodCallMessage(IMethodCallMessage mcm) {
  294. _serverType = Type.GetType(mcm.TypeName, true);
  295. if (mcm.MethodSignature != null)
  296. _methodCallInfo = _serverType.GetMethod(mcm.MethodName,
  297. BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
  298. null, (Type []) mcm.MethodSignature, null);
  299. else
  300. _methodCallInfo = _serverType.GetMethod(mcm.MethodName,
  301. BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
  302. _methodCallParameters = _methodCallInfo.GetParameters();
  303. }
  304. Header[] BuildMessageHeaders (IMethodMessage msg)
  305. {
  306. ArrayList headers = new ArrayList (1);
  307. foreach (string key in msg.Properties.Keys)
  308. {
  309. switch (key) {
  310. case "__Uri":
  311. case "__MethodName":
  312. case "__TypeName":
  313. case "__Args":
  314. case "__OutArgs":
  315. case "__Return":
  316. case "__MethodSignature":
  317. case "__CallContext":
  318. continue;
  319. default:
  320. object value = msg.Properties [key];
  321. if (value != null)
  322. headers.Add (new Header (key, value, false, "http://schemas.microsoft.com/clr/soap/messageProperties"));
  323. break;
  324. }
  325. }
  326. if (RemotingServices.IsMethodOverloaded (msg))
  327. headers.Add (new Header ("__MethodSignature", msg.MethodSignature, false, "http://schemas.microsoft.com/clr/soap/messageProperties"));
  328. if (msg.LogicalCallContext != null && msg.LogicalCallContext.HasInfo)
  329. headers.Add (new Header ("__CallContext", msg.LogicalCallContext, false, "http://schemas.microsoft.com/clr/soap/messageProperties"));
  330. if (headers.Count == 0) return null;
  331. return (Header[]) headers.ToArray (typeof(Header));
  332. }
  333. object GetNullValue (Type paramType)
  334. {
  335. switch (Type.GetTypeCode (paramType))
  336. {
  337. case TypeCode.Boolean: return false;
  338. case TypeCode.Byte: return (byte)0;
  339. case TypeCode.Char: return '\0';
  340. case TypeCode.Decimal: return (decimal)0;
  341. case TypeCode.Double: return (double)0;
  342. case TypeCode.Int16: return (short)0;
  343. case TypeCode.Int32: return (int)0;
  344. case TypeCode.Int64: return (long)0;
  345. case TypeCode.SByte: return (sbyte)0;
  346. case TypeCode.Single: return (float)0;
  347. case TypeCode.UInt16: return (ushort)0;
  348. case TypeCode.UInt32: return (uint)0;
  349. case TypeCode.UInt64: return (ulong)0;
  350. default: return null;
  351. }
  352. }
  353. }
  354. }