Xamarin Public Jenkins (auto-signing) 7d7f676260 Imported Upstream version 5.16.0.100
Former-commit-id: 38faa55fb9669e35e7d8448b15c25dc447f25767
2018-08-07 15:19:03 +00:00

534 lines
18 KiB
C#

//
// DefaultMessageOperationFormatter.cs
//
// Author:
// Atsushi Enomoto <atsushi@ximian.com>
// Eyal Alaluf <eyala@mainsoft.com>
//
// Copyright (C) 2005-2010 Novell, Inc. http://www.novell.com
// Copyright (C) 2008 Mainsoft Co. http://www.mainsoft.com
// Copyright 2011 Xamarin Inc (http://www.xamarin.com).
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace System.ServiceModel.Dispatcher
{
// This type is introduced for moonlight compatibility.
internal class OperationFormatter
: IDispatchMessageFormatter, IClientMessageFormatter
{
BaseMessagesFormatter impl;
string operation_name;
public OperationFormatter (OperationDescription od, bool isRpc, bool isEncoded)
{
Validate (od, isRpc, isEncoded);
impl = BaseMessagesFormatter.Create (od);
operation_name = od.Name;
}
public string OperationName {
get { return operation_name; }
}
internal static bool IsValidReturnValue (MessagePartDescription part)
{
return part != null && part.Type != typeof (void);
}
internal static void Validate (OperationDescription od, bool isRpc, bool isEncoded)
{
bool hasVoid = false;
foreach (var md in od.Messages) {
if (md.IsTypedMessage || md.IsUntypedMessage) {
if (isRpc && !isEncoded)
throw new InvalidOperationException ("Message with action {0} is either strongly-typed or untyped, but defined as RPC and encoded.");
if (isRpc && hasVoid)
throw new InvalidOperationException (String.Format ("This operation '{0}' is defined as RPC and contains a message with void, which is not allowed.", od.Name));
} else {
hasVoid |= md.IsVoid;
}
}
}
public object DeserializeReply (Message message, object [] parameters)
{
return impl.DeserializeReply (message, parameters);
}
public Message SerializeRequest (MessageVersion messageVersion, object [] parameters)
{
return impl.SerializeRequest (messageVersion, parameters);
}
public void DeserializeRequest (Message message, object [] parameters)
{
impl.DeserializeRequest (message, parameters);
}
public Message SerializeReply (MessageVersion messageVersion, object [] parameters, object result)
{
return impl.SerializeReply (messageVersion, parameters, result);
}
}
internal abstract class BaseMessagesFormatter
: IDispatchMessageFormatter, IClientMessageFormatter
{
MessageDescriptionCollection messages;
bool isAsync;
ParameterInfo [] requestMethodParams;
ParameterInfo [] replyMethodParams;
List<Type> operation_known_types = new List<Type> ();
public BaseMessagesFormatter (MessageDescriptionCollection messages)
{
this.messages = messages;
}
public BaseMessagesFormatter (OperationDescription desc)
: this (desc.Messages)
{
if (desc.SyncMethod != null)
{
isAsync = false;
requestMethodParams = replyMethodParams = desc.SyncMethod.GetParameters ();
return;
}
isAsync = true;
ParameterInfo [] methodParams = desc.BeginMethod.GetParameters ();
requestMethodParams = new ParameterInfo [methodParams.Length - 2];
Array.Copy (methodParams, requestMethodParams, requestMethodParams.Length);
methodParams = desc.EndMethod.GetParameters ();
replyMethodParams = new ParameterInfo [methodParams.Length - 1];
Array.Copy (methodParams, replyMethodParams, replyMethodParams.Length);
operation_known_types.AddRange (desc.KnownTypes);
}
// FIXME: this should be refactored and eliminated.
// XmlSerializerFormatAttribute and DataContractFormatAttribute
// should be handled at ContractDescription.GetContract (to fill
// IOperationBehavior for each).
//
// Fixing the issue above should also fix "Formatter is already filled at initial state" issue described in EndpointDispatcher.cs and ContractDescription.cs.
public static BaseMessagesFormatter Create (OperationDescription desc)
{
MethodInfo attrProvider = desc.SyncMethod ?? desc.BeginMethod;
object [] attrs;
attrs = attrProvider.GetCustomAttributes (typeof (XmlSerializerFormatAttribute), false);
if (attrs != null && attrs.Length > 0)
return new XmlMessagesFormatter (desc, (XmlSerializerFormatAttribute) attrs [0]);
attrs = attrProvider.GetCustomAttributes (typeof (DataContractFormatAttribute), false);
DataContractFormatAttribute dataAttr = null;
if (attrs != null && attrs.Length > 0)
dataAttr = (DataContractFormatAttribute) attrs [0];
return new DataContractMessagesFormatter (desc, dataAttr);
}
public IEnumerable<Type> OperationKnownTypes {
get { return operation_known_types; }
}
protected abstract Message PartsToMessage (
MessageDescription md, MessageVersion version, string action, object [] parts);
protected abstract object [] MessageToParts (MessageDescription md, Message message);
protected abstract Dictionary<MessageHeaderDescription,object> MessageToHeaderObjects (MessageDescription md, Message message);
public Message SerializeRequest (
MessageVersion version, object [] parameters)
{
MessageDescription md = null;
foreach (MessageDescription mdi in messages)
if (mdi.IsRequest)
md = mdi;
object [] parts = CreatePartsArray (md.Body);
var headers = md.Headers.Count > 0 ? new Dictionary<MessageHeaderDescription,object> () : null;
if (md.MessageType != null)
MessageObjectToParts (md, parameters [0], headers, parts);
else {
int index = 0;
foreach (ParameterInfo pi in requestMethodParams)
if (!pi.IsOut)
parts [index++] = parameters [pi.Position];
}
var msg = PartsToMessage (md, version, md.Action, parts);
if (headers != null)
foreach (var pair in headers)
if (pair.Value != null)
msg.Headers.Add (CreateHeader (pair.Key, pair.Value));
return msg;
}
public Message SerializeReply (
MessageVersion version, object [] parameters, object result)
{
// use_response_converter
MessageDescription md = null;
foreach (MessageDescription mdi in messages)
if (!mdi.IsRequest)
md = mdi;
object [] parts = CreatePartsArray (md.Body);
var headers = md.Headers.Count > 0 ? new Dictionary<MessageHeaderDescription,object> () : null;
if (md.MessageType != null)
MessageObjectToParts (md, result, headers, parts);
else {
if (HasReturnValue (md.Body))
parts [0] = result;
int index = ParamsOffset (md.Body);
int paramsIdx = 0;
foreach (ParameterInfo pi in replyMethodParams)
if (pi.IsOut || pi.ParameterType.IsByRef)
parts [index++] = parameters [paramsIdx++];
}
string action = version.Addressing == AddressingVersion.None ? null : md.Action;
var msg = PartsToMessage (md, version, action, parts);
if (headers != null)
foreach (var pair in headers)
if (pair.Value != null)
msg.Headers.Add (CreateHeader (pair.Key, pair.Value));
return msg;
}
MessageHeader CreateHeader (MessageHeaderDescription mh, object value)
{
return MessageHeader.CreateHeader (mh.Name, mh.Namespace, value, mh.MustUnderstand, mh.Actor, mh.Relay);
}
public void DeserializeRequest (Message message, object [] parameters)
{
string action = message.Headers.Action;
MessageDescription md = messages.Find (action);
if (md == null)
throw new ActionNotSupportedException (String.Format ("Action '{0}' is not supported by this operation.", action));
var headers = MessageToHeaderObjects (md, message);
object [] parts = MessageToParts (md, message);
if (md.MessageType != null) {
#if MOBILE
parameters [0] = Activator.CreateInstance (md.MessageType);
#else
parameters [0] = Activator.CreateInstance (md.MessageType, true);
#endif
PartsToMessageObject (md, headers, parts, parameters [0]);
}
else
{
int index = 0;
foreach (ParameterInfo pi in requestMethodParams)
if (!pi.IsOut) {
parameters [index] = parts [index];
index++;
}
}
}
public object DeserializeReply (Message message, object [] parameters)
{
MessageDescription md = null;
foreach (MessageDescription mdi in messages)
if (!mdi.IsRequest)
md = mdi;
var headers = MessageToHeaderObjects (md, message);
object [] parts = MessageToParts (md, message);
if (md.MessageType != null) {
#if MOBILE
object msgObject = Activator.CreateInstance (md.MessageType);
#else
object msgObject = Activator.CreateInstance (md.MessageType, true);
#endif
PartsToMessageObject (md, headers, parts, msgObject);
return msgObject;
}
else {
int index = ParamsOffset (md.Body);
foreach (ParameterInfo pi in replyMethodParams) {
if (pi.IsOut || pi.ParameterType.IsByRef)
parameters [pi.Position] = parts [index++];
}
return HasReturnValue (md.Body) ? parts [0] : null;
}
}
void PartsToMessageObject (MessageDescription md, Dictionary<MessageHeaderDescription,object> headers, object [] parts, object msgObject)
{
if (headers != null)
foreach (var pair in headers) {
var mi = pair.Key.MemberInfo;
if (mi is FieldInfo)
((FieldInfo) mi).SetValue (msgObject, pair.Value);
else
((PropertyInfo) mi).SetValue (msgObject, pair.Value, null);
}
var l = new List<MessagePartDescription> (md.Body.Parts);
if (md.Body.ReturnValue != null)
l.Add (md.Body.ReturnValue);
foreach (MessagePartDescription partDesc in l)
if (partDesc.MemberInfo is FieldInfo)
((FieldInfo) partDesc.MemberInfo).SetValue (msgObject, parts [partDesc.Index]);
else if (partDesc.MemberInfo is PropertyInfo)
((PropertyInfo) partDesc.MemberInfo).SetValue (msgObject, parts [partDesc.Index], null);
// otherwise, it could be null (in case of undefined return value in MessageContract)
}
void MessageObjectToParts (MessageDescription md, object msgObject, Dictionary<MessageHeaderDescription,object> headers, object [] parts)
{
foreach (var headDesc in md.Headers) {
var mi = headDesc.MemberInfo;
if (mi is FieldInfo)
headers [headDesc] = ((FieldInfo) mi).GetValue (msgObject);
else
headers [headDesc] = ((PropertyInfo) mi).GetValue (msgObject, null);
}
var l = new List<MessagePartDescription> (md.Body.Parts);
if (md.Body.ReturnValue != null)
l.Add (md.Body.ReturnValue);
foreach (MessagePartDescription partDesc in l) {
if (partDesc.MemberInfo == null)
continue;
if (partDesc.MemberInfo is FieldInfo)
parts [partDesc.Index] = ((FieldInfo) partDesc.MemberInfo).GetValue (msgObject);
else
parts [partDesc.Index] = ((PropertyInfo) partDesc.MemberInfo).GetValue (msgObject, null);
}
}
internal static bool HasReturnValue (MessageBodyDescription desc)
{
return desc.ReturnValue != null && desc.ReturnValue.Type != typeof (void);
}
protected static int ParamsOffset (MessageBodyDescription desc)
{
return HasReturnValue (desc) ? 1 : 0;
}
protected static object [] CreatePartsArray (MessageBodyDescription desc)
{
if (HasReturnValue (desc))
return new object [desc.Parts.Count + 1];
return new object [desc.Parts.Count];
}
}
class DataContractMessagesFormatter : BaseMessagesFormatter
{
DataContractFormatAttribute attr;
#if !MOBILE
DataContractSerializerOperationBehavior serializerBehavior;
#endif
public DataContractMessagesFormatter (OperationDescription desc, DataContractFormatAttribute attr)
: base (desc)
{
#if !MOBILE
this.serializerBehavior = desc.Behaviors.Find<DataContractSerializerOperationBehavior>();
#endif
this.attr = attr;
}
public DataContractMessagesFormatter (MessageDescriptionCollection messages, DataContractFormatAttribute attr)
: base (messages)
{
this.attr = attr;
}
Dictionary<MessagePartDescription, XmlObjectSerializer> serializers
= new Dictionary<MessagePartDescription,XmlObjectSerializer> ();
protected override Message PartsToMessage (
MessageDescription md, MessageVersion version, string action, object [] parts)
{
return Message.CreateMessage (version, action, new DataContractBodyWriter (md.Body, this, parts));
}
protected override Dictionary<MessageHeaderDescription,object> MessageToHeaderObjects (MessageDescription md, Message message)
{
if (message.IsEmpty || md.Headers.Count == 0)
return null;
var dic = new Dictionary<MessageHeaderDescription,object> ();
for (int i = 0; i < message.Headers.Count; i++) {
var r = message.Headers.GetReaderAtHeader (i);
var mh = md.Headers.FirstOrDefault (h => h.Name == r.LocalName && h.Namespace == r.NamespaceURI);
if (mh != null)
dic [mh] = ReadHeaderObject (mh.Type, GetSerializer (mh), r);
}
return dic;
}
protected override object [] MessageToParts (
MessageDescription md, Message message)
{
if (message.IsEmpty)
return null;
int offset = ParamsOffset (md.Body);
object [] parts = CreatePartsArray (md.Body);
XmlDictionaryReader r = message.GetReaderAtBodyContents ();
if (md.Body.WrapperName != null)
r.ReadStartElement (md.Body.WrapperName, md.Body.WrapperNamespace);
for (r.MoveToContent (); r.NodeType == XmlNodeType.Element; r.MoveToContent ()) {
XmlQualifiedName key = new XmlQualifiedName (r.LocalName, r.NamespaceURI);
MessagePartDescription rv = md.Body.ReturnValue;
if (rv != null && rv.Name == key.Name && rv.Namespace == key.Namespace && rv.Type != typeof (void))
parts [0] = ReadMessagePart (md.Body.ReturnValue, r);
else if (md.Body.Parts.Contains (key)) {
MessagePartDescription p = md.Body.Parts [key];
parts [p.Index + offset] = ReadMessagePart (p, r);
}
else // Skip unknown elements
r.Skip ();
}
if (md.Body.WrapperName != null && !r.EOF)
r.ReadEndElement ();
return parts;
}
object ReadMessagePart (MessagePartDescription part, XmlDictionaryReader r)
{
if (part.Type == typeof (Stream))
// FIXME: it seems TransferMode.Streamed* has different serialization than .Buffered. Need to differentiate serialization somewhere (not limited to here).
return new MemoryStream (Convert.FromBase64String (r.ReadElementContentAsString (part.Name, part.Namespace)));
else
return GetSerializer (part).ReadObject (r);
}
XmlObjectSerializer GetSerializer (MessagePartDescription partDesc)
{
if (!serializers.ContainsKey (partDesc))
#if !MOBILE
if (serializerBehavior != null)
serializers [partDesc] = serializerBehavior.CreateSerializer(
partDesc.Type, partDesc.Name, partDesc.Namespace, OperationKnownTypes as IList<Type>);
else
#endif
serializers [partDesc] = new DataContractSerializer (
partDesc.Type, partDesc.Name, partDesc.Namespace, OperationKnownTypes);
return serializers [partDesc];
}
object ReadHeaderObject (Type type, XmlObjectSerializer serializer, XmlDictionaryReader reader)
{
// FIXME: it's a nasty workaround just to avoid UniqueId output as a string.
// Seealso MessageHeader.DefaultMessageHeader.OnWriteHeaderContents().
// Note that msg.Headers.GetHeader<UniqueId> () simply fails (on .NET too) and it is useless. The API is lame by design.
if (type == typeof (UniqueId))
return new UniqueId (reader.ReadElementContentAsString ());
else
return serializer.ReadObject (reader);
}
class DataContractBodyWriter : BodyWriter
{
MessageBodyDescription desc;
object [] parts;
DataContractMessagesFormatter parent;
public DataContractBodyWriter (MessageBodyDescription desc, DataContractMessagesFormatter parent, object [] parts)
: base (false)
{
this.desc = desc;
this.parent = parent;
this.parts = parts;
}
protected override void OnWriteBodyContents (XmlDictionaryWriter writer)
{
int offset = HasReturnValue (desc) ? 1 : 0;
if (desc.WrapperName != null)
writer.WriteStartElement (desc.WrapperName, desc.WrapperNamespace);
if (HasReturnValue (desc))
WriteMessagePart (writer, desc, desc.ReturnValue, parts [0]);
foreach (MessagePartDescription partDesc in desc.Parts)
WriteMessagePart (writer, desc, partDesc, parts [partDesc.Index + offset]);
if (desc.WrapperName != null)
writer.WriteEndElement ();
}
void WriteMessagePart (
XmlDictionaryWriter writer, MessageBodyDescription desc, MessagePartDescription partDesc, object obj)
{
// FIXME: it seems TransferMode.Streamed* has different serialization than .Buffered. Need to differentiate serialization somewhere (not limited to here).
if (partDesc.Type == typeof (Stream)) {
writer.WriteStartElement (partDesc.Name, partDesc.Namespace);
writer.WriteValue (new StreamProvider ((Stream) obj));
writer.WriteEndElement ();
}
else
parent.GetSerializer (partDesc).WriteObject (writer, obj);
}
}
class StreamProvider : IStreamProvider
{
Stream s;
bool busy;
public StreamProvider (Stream s)
{
this.s = s;
}
public Stream GetStream ()
{
if (busy)
throw new InvalidOperationException ("Stream is already in use.");
busy = true;
return s;
}
public void ReleaseStream (Stream stream)
{
if (stream == null)
throw new ArgumentNullException ("stream");
if (this.s != stream)
throw new ArgumentException ("Incorrect parameter stream");
busy = false;
}
}
}
}