SoapMessageFormatter.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  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. // used by the client
  39. internal IMessage FormatResponse(ISoapMessage soapMsg,
  40. IMethodCallMessage mcm)
  41. {
  42. IMessage rtnMsg;
  43. if(soapMsg.MethodName == "Fault") {
  44. // an exception was thrown by the server
  45. Exception e = new SerializationException();
  46. int i = Array.IndexOf(soapMsg.ParamNames, "detail");
  47. if(_serverFaultExceptionField != null)
  48. // todo: revue this 'cause it's not safe
  49. e = (Exception) _serverFaultExceptionField.GetValue(
  50. soapMsg.ParamValues[i]);
  51. rtnMsg = new ReturnMessage((System.Exception)e, mcm);
  52. }
  53. else {
  54. object rtnObject = null;
  55. ArrayList outParams = new ArrayList();
  56. int nbOutParams = 0;
  57. RemMessageType messageType;
  58. // Get the output of the function if it is not *void*
  59. if(_methodCallInfo.ReturnType != typeof(void)){
  60. int index = Array.IndexOf(soapMsg.ParamNames, "return");
  61. rtnObject = soapMsg.ParamValues[index];
  62. if(rtnObject is IConvertible)
  63. rtnObject = Convert.ChangeType(
  64. rtnObject,
  65. _methodCallInfo.ReturnType);
  66. }
  67. // check if there are *out* parameters
  68. foreach(ParameterInfo paramInfo in _methodCallParameters) {
  69. if(paramInfo.ParameterType.IsByRef || paramInfo.IsOut) {
  70. int index = Array.IndexOf(soapMsg.ParamNames, paramInfo.Name);
  71. nbOutParams++;
  72. object outParam = soapMsg.ParamValues[index];
  73. if(outParam is IConvertible)
  74. outParam = Convert.ChangeType(
  75. outParam,
  76. paramInfo.ParameterType.GetElementType());
  77. outParams.Add(outParam);
  78. }
  79. // else outParams.Insert(paramInfo.Position, null);
  80. }
  81. rtnMsg = new ReturnMessage(
  82. rtnObject,
  83. (object[]) outParams.ToArray(typeof(object)),
  84. nbOutParams,
  85. mcm.LogicalCallContext,
  86. mcm);
  87. }
  88. return rtnMsg;
  89. }
  90. // used by the client
  91. internal SoapMessage BuildSoapMessageFromMethodCall(
  92. IMethodCallMessage mcm,
  93. out ITransportHeaders requestHeaders)
  94. {
  95. requestHeaders = new TransportHeaders();
  96. SoapMessage soapMsg = new SoapMessage();
  97. string uri = mcm.Uri;
  98. GetInfoFromMethodCallMessage(mcm);
  99. // Format the SoapMessage that will be used to create the RPC
  100. soapMsg.MethodName = mcm.MethodName;
  101. int count = mcm.ArgCount;
  102. ArrayList paramNames = new ArrayList(_methodCallParameters.Length);
  103. ArrayList paramTypes = new ArrayList(_methodCallParameters.Length);
  104. ArrayList paramValues = new ArrayList(_methodCallParameters.Length);
  105. // Add the function parameters to the SoapMessage class
  106. foreach(ParameterInfo paramInfo in _methodCallParameters) {
  107. if(!paramInfo.IsOut) {
  108. paramNames.Add(paramInfo.Name);
  109. paramTypes.Add(paramInfo.ParameterType);
  110. paramValues.Add(mcm.Args[paramInfo.Position]);
  111. }
  112. }
  113. soapMsg.ParamNames = (string[]) paramNames.ToArray(typeof(string));
  114. soapMsg.ParamTypes = (Type[]) paramTypes.ToArray(typeof(Type));
  115. soapMsg.ParamValues = (object[]) paramValues.ToArray(typeof(object));
  116. soapMsg.XmlNameSpace = SoapServices.GetXmlNamespaceForMethodCall(_methodCallInfo);
  117. // Format the transport headers
  118. requestHeaders["Content-Type"] = "text/xml; charset=\"utf-8\"";
  119. requestHeaders["SOAPAction"] = "\""+
  120. SoapServices.GetSoapActionFromMethodBase(_methodCallInfo)+"\"";
  121. requestHeaders[CommonTransportKeys.RequestUri] = mcm.Uri;
  122. return soapMsg;
  123. }
  124. // used by the server
  125. internal IMessage BuildMethodCallFromSoapMessage(SoapMessage soapMessage, string uri) {
  126. ArrayList headersList = new ArrayList();
  127. headersList.Add(new Header("__Uri", uri));
  128. headersList.Add(new Header("__MethodName", soapMessage.MethodName));
  129. string typeNamespace, assemblyName;
  130. bool b = SoapServices.DecodeXmlNamespaceForClrTypeNamespace(soapMessage.XmlNameSpace, out typeNamespace, out assemblyName);
  131. _serverType = RemotingServices.GetServerTypeForUri(uri);
  132. headersList.Add(new Header("__TypeName", _serverType.FullName, false));
  133. _xmlNamespace = soapMessage.XmlNameSpace;
  134. RemMessageType messageType;
  135. _methodCallInfo = _serverType.GetMethod(soapMessage.MethodName);
  136. // the *out* parameters aren't serialized
  137. // have to add them here
  138. _methodCallParameters = _methodCallInfo.GetParameters();
  139. object[] args = new object[_methodCallParameters.Length];
  140. foreach(ParameterInfo paramInfo in _methodCallParameters)
  141. {
  142. Type paramType = (paramInfo.ParameterType.IsByRef ? paramInfo.ParameterType.GetElementType() : paramInfo.ParameterType);
  143. if(paramInfo.IsOut) {
  144. args [paramInfo.Position] = GetNullValue (paramType);
  145. }
  146. else{
  147. int index = Array.IndexOf(soapMessage.ParamNames, paramInfo.Name);
  148. if(soapMessage.ParamValues[index] is IConvertible)
  149. soapMessage.ParamValues[index] = Convert.ChangeType(
  150. soapMessage.ParamValues[index],
  151. paramType);
  152. args [paramInfo.Position] = soapMessage.ParamValues[index];
  153. }
  154. }
  155. headersList.Add(new Header("__Args", args, false));
  156. Header[] headers = (Header[])headersList.ToArray(typeof(Header));
  157. // build the MethodCall from the headers
  158. MethodCall mthCall = new MethodCall(headers);
  159. return (IMessage)mthCall;
  160. }
  161. // used by the server
  162. internal object BuildSoapMessageFromMethodResponse(IMethodReturnMessage mrm, out ITransportHeaders responseHeaders)
  163. {
  164. responseHeaders = new TransportHeaders();
  165. if(mrm.Exception == null) {
  166. // *normal* function return
  167. SoapMessage soapMessage = new SoapMessage();
  168. // fill the transport headers
  169. responseHeaders["Content-Type"] = "text/xml; charset=\"utf-8\"";
  170. // build the SoapMessage
  171. ArrayList paramNames = new ArrayList();
  172. ArrayList paramValues = new ArrayList();
  173. ArrayList paramTypes = new ArrayList();
  174. soapMessage.MethodName = mrm.MethodName+"Response";
  175. if(mrm.ReturnValue != null && mrm.ReturnValue.GetType() != typeof(void)) {
  176. paramNames.Add("return");
  177. paramValues.Add(mrm.ReturnValue);
  178. paramTypes.Add(mrm.ReturnValue.GetType());
  179. }
  180. for(int i = 0; i < mrm.OutArgCount; i++){
  181. paramNames.Add(mrm.GetOutArgName(i));
  182. paramValues.Add(mrm.GetOutArg(i));
  183. if(mrm.GetOutArg(i) != null) paramTypes.Add(mrm.GetOutArg(i).GetType());
  184. }
  185. soapMessage.ParamNames = (string[]) paramNames.ToArray(typeof(string));
  186. soapMessage.ParamValues = (object[]) paramValues.ToArray(typeof(object));
  187. soapMessage.ParamTypes = (Type[]) paramTypes.ToArray(typeof(Type));
  188. soapMessage.XmlNameSpace = _xmlNamespace;
  189. return soapMessage;
  190. }
  191. else {
  192. // an Exception was thrown while executing the function
  193. responseHeaders["__HttpStatusCode"] = "400";
  194. responseHeaders["__HttpReasonPhrase"] = "Bad Request";
  195. // fill the transport headers
  196. responseHeaders["Content-Type"] = "text/xml; charset=\"utf-8\"";
  197. ServerFault serverFault = CreateServerFault(mrm.Exception);
  198. return new SoapFault("Server", String.Format(" **** {0} - {1}", mrm.Exception.GetType().ToString(), mrm.Exception.Message), null, serverFault);
  199. }
  200. }
  201. // used by the server when an exception is thrown
  202. // by the called function
  203. internal ServerFault CreateServerFault(Exception e) {
  204. // it's really strange here
  205. // a ServerFault object has a private System.Exception member called *exception*
  206. // (have a look at a MS Soap message when an exception occurs on the server)
  207. // but there is not public .ctor with an Exception as parameter...????....
  208. // (maybe an internal one). So I searched another way...
  209. ServerFault sf = (ServerFault) FormatterServices.GetUninitializedObject(typeof(ServerFault));
  210. MemberInfo[] mi = FormatterServices.GetSerializableMembers(typeof(ServerFault), new StreamingContext(StreamingContextStates.All));
  211. FieldInfo fi;
  212. object[] mv = new object[mi.Length];
  213. for(int i = 0; i < mi.Length; i++) {
  214. fi = mi[i] as FieldInfo;
  215. if(fi != null && fi.FieldType == typeof(Exception)) mv[i] = e;
  216. }
  217. sf = (ServerFault) FormatterServices.PopulateObjectMembers(sf, mi, mv);
  218. return sf;
  219. }
  220. internal void GetInfoFromMethodCallMessage(IMethodCallMessage mcm) {
  221. _serverType = Type.GetType(mcm.TypeName, true);
  222. if (mcm.MethodSignature != null)
  223. _methodCallInfo = _serverType.GetMethod(mcm.MethodName,
  224. BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
  225. null, (Type []) mcm.MethodSignature, null);
  226. else
  227. _methodCallInfo = _serverType.GetMethod(mcm.MethodName,
  228. BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
  229. _methodCallParameters = _methodCallInfo.GetParameters();
  230. }
  231. object GetNullValue (Type paramType)
  232. {
  233. switch (Type.GetTypeCode (paramType))
  234. {
  235. case TypeCode.Boolean: return false;
  236. case TypeCode.Byte: return (byte)0;
  237. case TypeCode.Char: return '\0';
  238. case TypeCode.Decimal: return (decimal)0;
  239. case TypeCode.Double: return (double)0;
  240. case TypeCode.Int16: return (short)0;
  241. case TypeCode.Int32: return (int)0;
  242. case TypeCode.Int64: return (long)0;
  243. case TypeCode.SByte: return (sbyte)0;
  244. case TypeCode.Single: return (float)0;
  245. case TypeCode.UInt16: return (ushort)0;
  246. case TypeCode.UInt32: return (uint)0;
  247. case TypeCode.UInt64: return (ulong)0;
  248. default: return null;
  249. }
  250. }
  251. }
  252. }