SoapMessageFormatter.cs 13 KB

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