1 // created on 03/04/2003 at 14:09
3 // System.Runtime.Remoting.Channels.SoapMessageFormatter
5 // Author: Jean-Marc Andre (jean-marc.andre@polymtl.ca)
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System
.Collections
;
32 using System
.Reflection
;
33 using System
.Runtime
.Remoting
.Messaging
;
34 using System
.Runtime
.Serialization
;
35 using System
.Runtime
.Serialization
.Formatters
;
39 namespace System
.Runtime
.Remoting
.Channels
{
41 MethodCall
, MethodResponse
, ServerFault
, NotRecognize
44 internal class SoapMessageFormatter
{
45 private static FieldInfo _serverFaultExceptionField
;
46 private Type _serverType
;
47 private MethodInfo _methodCallInfo
;
48 private ParameterInfo
[] _methodCallParameters
;
49 private string _xmlNamespace
;
51 static SoapMessageFormatter() {
52 // Get the ServerFault exception field FieldInfo that
53 // will be used later if an exception occurs on the server
54 MemberInfo
[] mi
= FormatterServices
.GetSerializableMembers(typeof(ServerFault
), new StreamingContext(StreamingContextStates
.All
));
56 for(int i
= 0; i
< mi
.Length
; i
++){
57 fi
= mi
[i
] as FieldInfo
;
58 if(fi
!= null && fi
.FieldType
== typeof(Exception
)){
59 _serverFaultExceptionField
= fi
;
65 internal SoapMessageFormatter() {
69 internal IMessage
FormatFault (SoapFault fault
, IMethodCallMessage mcm
)
71 ServerFault sf
= fault
.Detail
as ServerFault
;
75 if(_serverFaultExceptionField
!= null)
76 e
= (Exception
) _serverFaultExceptionField
.GetValue(sf
);
79 e
= new RemotingException (fault
.FaultString
);
81 return new ReturnMessage((System
.Exception
)e
, mcm
);
85 internal IMessage
FormatResponse(ISoapMessage soapMsg
, IMethodCallMessage mcm
)
89 if(soapMsg
.MethodName
== "Fault") {
90 // an exception was thrown by the server
91 Exception e
= new SerializationException();
92 int i
= Array
.IndexOf(soapMsg
.ParamNames
, "detail");
93 if(_serverFaultExceptionField
!= null)
94 // todo: revue this 'cause it's not safe
95 e
= (Exception
) _serverFaultExceptionField
.GetValue(
96 soapMsg
.ParamValues
[i
]);
98 rtnMsg
= new ReturnMessage((System
.Exception
)e
, mcm
);
101 object rtnObject
= null;
102 RemMessageType messageType
;
104 // Get the output of the function if it is not *void*
105 if(_methodCallInfo
.ReturnType
!= typeof(void)){
106 int index
= Array
.IndexOf(soapMsg
.ParamNames
, "return");
107 rtnObject
= soapMsg
.ParamValues
[index
];
108 if(rtnObject
is IConvertible
)
109 rtnObject
= Convert
.ChangeType(
111 _methodCallInfo
.ReturnType
);
114 object[] outParams
= new object [_methodCallParameters
.Length
];
117 // check if there are *out* parameters
118 foreach(ParameterInfo paramInfo
in _methodCallParameters
) {
120 if(paramInfo
.ParameterType
.IsByRef
|| paramInfo
.IsOut
) {
121 int index
= Array
.IndexOf(soapMsg
.ParamNames
, paramInfo
.Name
);
122 object outParam
= soapMsg
.ParamValues
[index
];
123 if(outParam
is IConvertible
)
124 outParam
= Convert
.ChangeType (outParam
, paramInfo
.ParameterType
.GetElementType());
125 outParams
[n
] = outParam
;
128 outParams
[n
] = null;
132 Header
[] headers
= new Header
[2 + (soapMsg
.Headers
!= null ? soapMsg
.Headers
.Length
: 0)];
133 headers
[0] = new Header ("__Return", rtnObject
);
134 headers
[1] = new Header ("__OutArgs", outParams
);
136 if (soapMsg
.Headers
!= null)
137 soapMsg
.Headers
.CopyTo (headers
, 2);
139 rtnMsg
= new MethodResponse (headers
, mcm
);
144 // used by the client
145 internal SoapMessage
BuildSoapMessageFromMethodCall(
146 IMethodCallMessage mcm
,
147 out ITransportHeaders requestHeaders
)
150 requestHeaders
= new TransportHeaders();
151 SoapMessage soapMsg
= new SoapMessage();
152 string uri
= mcm
.Uri
;
154 GetInfoFromMethodCallMessage(mcm
);
156 // Format the SoapMessage that will be used to create the RPC
157 soapMsg
.MethodName
= mcm
.MethodName
;
158 int count
= mcm
.ArgCount
;
159 ArrayList paramNames
= new ArrayList(_methodCallParameters
.Length
);
160 ArrayList paramTypes
= new ArrayList(_methodCallParameters
.Length
);
161 ArrayList paramValues
= new ArrayList(_methodCallParameters
.Length
);
163 // Add the function parameters to the SoapMessage class
164 foreach(ParameterInfo paramInfo
in _methodCallParameters
) {
165 if (!(paramInfo
.IsOut
&& paramInfo
.ParameterType
.IsByRef
)) {
166 Type t
= paramInfo
.ParameterType
;
167 if (t
.IsByRef
) t
= t
.GetElementType ();
168 paramNames
.Add(paramInfo
.Name
);
170 paramValues
.Add(mcm
.Args
[paramInfo
.Position
]);
173 soapMsg
.ParamNames
= (string[]) paramNames
.ToArray(typeof(string));
174 soapMsg
.ParamTypes
= (Type
[]) paramTypes
.ToArray(typeof(Type
));
175 soapMsg
.ParamValues
= (object[]) paramValues
.ToArray(typeof(object));
176 soapMsg
.XmlNameSpace
= SoapServices
.GetXmlNamespaceForMethodCall(_methodCallInfo
);
177 soapMsg
.Headers
= BuildMessageHeaders (mcm
);
179 // Format the transport headers
180 requestHeaders
["Content-Type"] = "text/xml; charset=\"utf-8\"";
181 requestHeaders
["SOAPAction"] = "\""+
182 SoapServices
.GetSoapActionFromMethodBase(_methodCallInfo
)+"\"";
183 requestHeaders
[CommonTransportKeys
.RequestUri
] = mcm
.Uri
;
189 // used by the server
190 internal IMessage
BuildMethodCallFromSoapMessage(SoapMessage soapMessage
, string uri
)
192 ArrayList headersList
= new ArrayList();
193 Type
[] signature
= null;
195 headersList
.Add(new Header("__Uri", uri
));
196 headersList
.Add(new Header("__MethodName", soapMessage
.MethodName
));
197 string typeNamespace
, assemblyName
;
198 bool b
= SoapServices
.DecodeXmlNamespaceForClrTypeNamespace(soapMessage
.XmlNameSpace
, out typeNamespace
, out assemblyName
);
200 _serverType
= RemotingServices
.GetServerTypeForUri(uri
);
201 headersList
.Add(new Header("__TypeName", _serverType
.FullName
, false));
203 if (soapMessage
.Headers
!= null) {
204 foreach (Header h
in soapMessage
.Headers
) {
206 if (h
.Name
== "__MethodSignature")
207 signature
= (Type
[]) h
.Value
;
211 _xmlNamespace
= soapMessage
.XmlNameSpace
;
212 RemMessageType messageType
;
214 BindingFlags bflags
= BindingFlags
.Instance
| BindingFlags
.Public
| BindingFlags
.NonPublic
;
216 if (signature
== null)
217 _methodCallInfo
= _serverType
.GetMethod(soapMessage
.MethodName
, bflags
);
219 _methodCallInfo
= _serverType
.GetMethod(soapMessage
.MethodName
, bflags
, null, signature
, null);
221 // the *out* parameters aren't serialized
222 // have to add them here
223 _methodCallParameters
= _methodCallInfo
.GetParameters();
224 object[] args
= new object[_methodCallParameters
.Length
];
226 for (int n
=0; n
<_methodCallParameters
.Length
; n
++)
228 ParameterInfo paramInfo
= _methodCallParameters
[n
];
229 Type paramType
= (paramInfo
.ParameterType
.IsByRef
? paramInfo
.ParameterType
.GetElementType() : paramInfo
.ParameterType
);
231 if (paramInfo
.IsOut
&& paramInfo
.ParameterType
.IsByRef
) {
232 args
[n
] = GetNullValue (paramType
);
235 object val
= soapMessage
.ParamValues
[sn
++];
236 if(val
is IConvertible
)
237 args
[n
] = Convert
.ChangeType (val
, paramType
);
243 headersList
.Add(new Header("__Args", args
, false));
245 Header
[] headers
= (Header
[])headersList
.ToArray(typeof(Header
));
247 // build the MethodCall from the headers
248 MethodCall mthCall
= new MethodCall(headers
);
249 return (IMessage
)mthCall
;
252 // used by the server
253 internal object BuildSoapMessageFromMethodResponse(IMethodReturnMessage mrm
, out ITransportHeaders responseHeaders
)
255 responseHeaders
= new TransportHeaders();
257 if(mrm
.Exception
== null) {
258 // *normal* function return
260 SoapMessage soapMessage
= new SoapMessage();
262 // fill the transport headers
263 responseHeaders
["Content-Type"] = "text/xml; charset=\"utf-8\"";
265 // build the SoapMessage
266 ArrayList paramNames
= new ArrayList();
267 ArrayList paramValues
= new ArrayList();
268 ArrayList paramTypes
= new ArrayList();
269 soapMessage
.MethodName
= mrm
.MethodName
+"Response";
271 Type retType
= ((MethodInfo
)mrm
.MethodBase
).ReturnType
;
273 if(retType
!= typeof(void)) {
274 paramNames
.Add("return");
275 paramValues
.Add(mrm
.ReturnValue
);
276 if (mrm
.ReturnValue
!= null)
277 paramTypes
.Add(mrm
.ReturnValue
.GetType());
279 paramTypes
.Add(retType
);
282 for(int i
= 0; i
< mrm
.OutArgCount
; i
++){
283 paramNames
.Add(mrm
.GetOutArgName(i
));
284 paramValues
.Add(mrm
.GetOutArg(i
));
285 if(mrm
.GetOutArg(i
) != null) paramTypes
.Add(mrm
.GetOutArg(i
).GetType());
287 soapMessage
.ParamNames
= (string[]) paramNames
.ToArray(typeof(string));
288 soapMessage
.ParamValues
= (object[]) paramValues
.ToArray(typeof(object));
289 soapMessage
.ParamTypes
= (Type
[]) paramTypes
.ToArray(typeof(Type
));
290 soapMessage
.XmlNameSpace
= _xmlNamespace
;
291 soapMessage
.Headers
= BuildMessageHeaders (mrm
);
295 // an Exception was thrown while executing the function
296 responseHeaders
["__HttpStatusCode"] = "400";
297 responseHeaders
["__HttpReasonPhrase"] = "Bad Request";
298 // fill the transport headers
299 responseHeaders
["Content-Type"] = "text/xml; charset=\"utf-8\"";
300 ServerFault serverFault
= CreateServerFault(mrm
.Exception
);
301 return new SoapFault("Server", String
.Format(" **** {0} - {1}", mrm
.Exception
.GetType().ToString(), mrm
.Exception
.Message
), null, serverFault
);
305 internal SoapMessage
CreateSoapMessage (bool isRequest
)
307 if (isRequest
) return new SoapMessage ();
310 Type
[] types
= new Type
[_methodCallParameters
.Length
+ 1];
312 if (_methodCallInfo
.ReturnType
!= typeof(void)) {
313 types
[0] = _methodCallInfo
.ReturnType
;
317 foreach(ParameterInfo paramInfo
in _methodCallParameters
)
319 if (paramInfo
.ParameterType
.IsByRef
|| paramInfo
.IsOut
)
321 Type t
= paramInfo
.ParameterType
;
322 if (t
.IsByRef
) t
= t
.GetElementType();
326 SoapMessage sm
= new SoapMessage ();
327 sm
.ParamTypes
= types
;
331 // used by the server when an exception is thrown
332 // by the called function
333 internal ServerFault
CreateServerFault(Exception e
) {
334 // it's really strange here
335 // a ServerFault object has a private System.Exception member called *exception*
336 // (have a look at a MS Soap message when an exception occurs on the server)
337 // but there is not public .ctor with an Exception as parameter...????....
338 // (maybe an internal one). So I searched another way...
339 ServerFault sf
= (ServerFault
) FormatterServices
.GetUninitializedObject(typeof(ServerFault
));
340 MemberInfo
[] mi
= FormatterServices
.GetSerializableMembers(typeof(ServerFault
), new StreamingContext(StreamingContextStates
.All
));
343 object[] mv
= new object[mi
.Length
];
344 for(int i
= 0; i
< mi
.Length
; i
++) {
345 fi
= mi
[i
] as FieldInfo
;
346 if(fi
!= null && fi
.FieldType
== typeof(Exception
)) mv
[i
] = e
;
348 sf
= (ServerFault
) FormatterServices
.PopulateObjectMembers(sf
, mi
, mv
);
353 internal void GetInfoFromMethodCallMessage(IMethodCallMessage mcm
) {
354 _serverType
= Type
.GetType(mcm
.TypeName
, true);
356 if (mcm
.MethodSignature
!= null)
357 _methodCallInfo
= _serverType
.GetMethod(mcm
.MethodName
,
358 BindingFlags
.Instance
| BindingFlags
.Public
| BindingFlags
.NonPublic
,
359 null, (Type
[]) mcm
.MethodSignature
, null);
361 _methodCallInfo
= _serverType
.GetMethod(mcm
.MethodName
,
362 BindingFlags
.Instance
| BindingFlags
.Public
| BindingFlags
.NonPublic
);
364 _methodCallParameters
= _methodCallInfo
.GetParameters();
367 Header
[] BuildMessageHeaders (IMethodMessage msg
)
369 ArrayList headers
= new ArrayList (1);
370 foreach (string key
in msg
.Properties
.Keys
)
379 case "__MethodSignature":
380 case "__CallContext":
384 object value = msg
.Properties
[key
];
386 headers
.Add (new Header (key
, value, false, "http://schemas.microsoft.com/clr/soap/messageProperties"));
391 if (RemotingServices
.IsMethodOverloaded (msg
))
392 headers
.Add (new Header ("__MethodSignature", msg
.MethodSignature
, false, "http://schemas.microsoft.com/clr/soap/messageProperties"));
394 if (msg
.LogicalCallContext
!= null && msg
.LogicalCallContext
.HasInfo
)
395 headers
.Add (new Header ("__CallContext", msg
.LogicalCallContext
, false, "http://schemas.microsoft.com/clr/soap/messageProperties"));
397 if (headers
.Count
== 0) return null;
398 return (Header
[]) headers
.ToArray (typeof(Header
));
401 object GetNullValue (Type paramType
)
403 switch (Type
.GetTypeCode (paramType
))
405 case TypeCode
.Boolean
: return false;
406 case TypeCode
.Byte
: return (byte)0;
407 case TypeCode
.Char
: return '\0';
408 case TypeCode
.Decimal
: return (decimal)0;
409 case TypeCode
.Double
: return (double)0;
410 case TypeCode
.Int16
: return (short)0;
411 case TypeCode
.Int32
: return (int)0;
412 case TypeCode
.Int64
: return (long)0;
413 case TypeCode
.SByte
: return (sbyte)0;
414 case TypeCode
.Single
: return (float)0;
415 case TypeCode
.UInt16
: return (ushort)0;
416 case TypeCode
.UInt32
: return (uint)0;
417 case TypeCode
.UInt64
: return (ulong)0;
418 default: return null;