(DISTFILES): Comment out a few missing files.
[mono-project.git] / mcs / class / System.Runtime.Remoting / System.Runtime.Remoting.Channels / SoapMessageFormatter.cs
blob2de868c9bee50c443cf032b4511e21d47f1433d4
1 // created on 03/04/2003 at 14:09
2 //
3 // System.Runtime.Remoting.Channels.SoapMessageFormatter
4 //
5 // Author: Jean-Marc Andre (jean-marc.andre@polymtl.ca)
6 //
7 //
9 //
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:
17 //
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 //
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.
30 using System;
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 {
40 enum RemMessageType {
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));
55 FieldInfo fi;
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;
72 Exception e = null;
74 if (sf != null) {
75 if(_serverFaultExceptionField != null)
76 e = (Exception) _serverFaultExceptionField.GetValue(sf);
78 if (e == null)
79 e = new RemotingException (fault.FaultString);
81 return new ReturnMessage((System.Exception)e, mcm);
84 // used by the client
85 internal IMessage FormatResponse(ISoapMessage soapMsg, IMethodCallMessage mcm)
87 IMessage rtnMsg;
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);
100 else {
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(
110 rtnObject,
111 _methodCallInfo.ReturnType);
114 object[] outParams = new object [_methodCallParameters.Length];
115 int n=0;
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;
127 else
128 outParams [n] = null;
129 n++;
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);
141 return rtnMsg;
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);
169 paramTypes.Add(t);
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;
185 return soapMsg;
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) {
205 headersList.Add (h);
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);
218 else
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];
225 int sn = 0;
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);
234 else{
235 object val = soapMessage.ParamValues[sn++];
236 if(val is IConvertible)
237 args [n] = Convert.ChangeType (val, paramType);
238 else
239 args [n] = val;
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());
278 else
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);
292 return soapMessage;
294 else {
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 ();
309 int n = 0;
310 Type[] types = new Type [_methodCallParameters.Length + 1];
312 if (_methodCallInfo.ReturnType != typeof(void)) {
313 types[0] = _methodCallInfo.ReturnType;
314 n++;
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();
323 types [n++] = t;
326 SoapMessage sm = new SoapMessage ();
327 sm.ParamTypes = types;
328 return sm;
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));
342 FieldInfo fi;
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);
350 return sf;
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);
360 else
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)
372 switch (key) {
373 case "__Uri":
374 case "__MethodName":
375 case "__TypeName":
376 case "__Args":
377 case "__OutArgs":
378 case "__Return":
379 case "__MethodSignature":
380 case "__CallContext":
381 continue;
383 default:
384 object value = msg.Properties [key];
385 if (value != null)
386 headers.Add (new Header (key, value, false, "http://schemas.microsoft.com/clr/soap/messageProperties"));
387 break;
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;