SoapMessageFormatter.cs 15 KB

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