2016-08-03 10:59:49 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
namespace System.ServiceModel.Dispatcher
|
|
|
|
{
|
|
|
|
using System.IO;
|
|
|
|
using System.Runtime;
|
|
|
|
using System.ServiceModel;
|
|
|
|
using System.ServiceModel.Channels;
|
|
|
|
using System.ServiceModel.Description;
|
|
|
|
using System.ServiceModel.Diagnostics;
|
|
|
|
using System.Xml;
|
|
|
|
|
|
|
|
class StreamFormatter
|
|
|
|
{
|
|
|
|
string wrapperName;
|
|
|
|
string wrapperNS;
|
|
|
|
string partName;
|
|
|
|
string partNS;
|
|
|
|
int streamIndex;
|
|
|
|
bool isRequest;
|
|
|
|
string operationName;
|
|
|
|
const int returnValueIndex = -1;
|
|
|
|
|
|
|
|
internal static StreamFormatter Create(MessageDescription messageDescription, string operationName, bool isRequest)
|
|
|
|
{
|
|
|
|
MessagePartDescription streamPart = ValidateAndGetStreamPart(messageDescription, isRequest, operationName);
|
|
|
|
if (streamPart == null)
|
|
|
|
return null;
|
|
|
|
return new StreamFormatter(messageDescription, streamPart, operationName, isRequest);
|
|
|
|
}
|
|
|
|
|
|
|
|
StreamFormatter(MessageDescription messageDescription, MessagePartDescription streamPart, string operationName, bool isRequest)
|
|
|
|
{
|
|
|
|
if ((object)streamPart == (object)messageDescription.Body.ReturnValue)
|
|
|
|
this.streamIndex = returnValueIndex;
|
|
|
|
else
|
|
|
|
this.streamIndex = streamPart.Index;
|
|
|
|
wrapperName = messageDescription.Body.WrapperName;
|
|
|
|
wrapperNS = messageDescription.Body.WrapperNamespace;
|
|
|
|
partName = streamPart.Name;
|
|
|
|
partNS = streamPart.Namespace;
|
|
|
|
this.isRequest = isRequest;
|
|
|
|
this.operationName = operationName;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void Serialize(XmlDictionaryWriter writer, object[] parameters, object returnValue)
|
|
|
|
{
|
|
|
|
Stream streamValue = GetStreamAndWriteStartWrapperIfNecessary(writer, parameters, returnValue);
|
|
|
|
writer.WriteValue(new OperationStreamProvider(streamValue));
|
|
|
|
WriteEndWrapperIfNecessary(writer);
|
|
|
|
}
|
|
|
|
|
|
|
|
Stream GetStreamAndWriteStartWrapperIfNecessary(XmlDictionaryWriter writer, object[] parameters, object returnValue)
|
|
|
|
{
|
|
|
|
Stream streamValue = GetStreamValue(parameters, returnValue);
|
|
|
|
if (streamValue == null)
|
|
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(partName);
|
|
|
|
if (WrapperName != null)
|
|
|
|
writer.WriteStartElement(WrapperName, WrapperNamespace);
|
|
|
|
writer.WriteStartElement(PartName, PartNamespace);
|
|
|
|
return streamValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WriteEndWrapperIfNecessary(XmlDictionaryWriter writer)
|
|
|
|
{
|
|
|
|
writer.WriteEndElement();
|
|
|
|
if (wrapperName != null)
|
|
|
|
writer.WriteEndElement();
|
|
|
|
}
|
|
|
|
|
|
|
|
internal IAsyncResult BeginSerialize(XmlDictionaryWriter writer, object[] parameters, object returnValue, AsyncCallback callback, object state)
|
|
|
|
{
|
|
|
|
return new SerializeAsyncResult(this, writer, parameters, returnValue, callback, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void EndSerialize(IAsyncResult result)
|
|
|
|
{
|
|
|
|
SerializeAsyncResult.End(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
class SerializeAsyncResult : AsyncResult
|
|
|
|
{
|
|
|
|
static AsyncCompletion handleEndSerialize = new AsyncCompletion(HandleEndSerialize);
|
|
|
|
|
|
|
|
StreamFormatter streamFormatter;
|
|
|
|
XmlDictionaryWriter writer;
|
|
|
|
|
|
|
|
internal SerializeAsyncResult(StreamFormatter streamFormatter, XmlDictionaryWriter writer, object[] parameters, object returnValue,
|
|
|
|
AsyncCallback callback, object state)
|
|
|
|
: base(callback, state)
|
|
|
|
{
|
|
|
|
this.streamFormatter = streamFormatter;
|
|
|
|
this.writer = writer;
|
|
|
|
bool completeSelf = true;
|
|
|
|
|
|
|
|
Stream streamValue = streamFormatter.GetStreamAndWriteStartWrapperIfNecessary(writer, parameters, returnValue);
|
|
|
|
IAsyncResult result = writer.WriteValueAsync(new OperationStreamProvider(streamValue)).AsAsyncResult(PrepareAsyncCompletion(handleEndSerialize), this);
|
|
|
|
completeSelf = SyncContinue(result);
|
|
|
|
|
|
|
|
// Note: The current task implementation hard codes the "IAsyncResult.CompletedSynchronously" property to false, so this fast path will never
|
|
|
|
// be hit, and we will always hop threads. CSDMain #210220
|
|
|
|
if (completeSelf)
|
|
|
|
{
|
|
|
|
Complete(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool HandleEndSerialize(IAsyncResult result)
|
|
|
|
{
|
|
|
|
SerializeAsyncResult thisPtr = (SerializeAsyncResult)result.AsyncState;
|
|
|
|
thisPtr.streamFormatter.WriteEndWrapperIfNecessary(thisPtr.writer);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void End(IAsyncResult result)
|
|
|
|
{
|
|
|
|
AsyncResult.End<SerializeAsyncResult>(result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal void Deserialize(object[] parameters, ref object retVal, Message message)
|
|
|
|
{
|
|
|
|
SetStreamValue(parameters, ref retVal, new MessageBodyStream(message, WrapperName, WrapperNamespace, PartName, PartNamespace, isRequest));
|
|
|
|
}
|
|
|
|
|
|
|
|
internal string WrapperName
|
|
|
|
{
|
|
|
|
get { return wrapperName; }
|
|
|
|
set { wrapperName = value; }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal string WrapperNamespace
|
|
|
|
{
|
|
|
|
get { return wrapperNS; }
|
|
|
|
set { wrapperNS = value; }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal string PartName
|
|
|
|
{
|
|
|
|
get { return partName; }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal string PartNamespace
|
|
|
|
{
|
|
|
|
get { return partNS; }
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Stream GetStreamValue(object[] parameters, object returnValue)
|
|
|
|
{
|
|
|
|
if (streamIndex == returnValueIndex)
|
|
|
|
return (Stream)returnValue;
|
|
|
|
return (Stream)parameters[streamIndex];
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetStreamValue(object[] parameters, ref object returnValue, Stream streamValue)
|
|
|
|
{
|
|
|
|
if (streamIndex == returnValueIndex)
|
|
|
|
returnValue = streamValue;
|
|
|
|
else
|
|
|
|
parameters[streamIndex] = streamValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
static MessagePartDescription ValidateAndGetStreamPart(MessageDescription messageDescription, bool isRequest, string operationName)
|
|
|
|
{
|
|
|
|
MessagePartDescription part = GetStreamPart(messageDescription);
|
|
|
|
if (part != null)
|
|
|
|
return part;
|
|
|
|
if (HasStream(messageDescription))
|
|
|
|
{
|
|
|
|
if (messageDescription.IsTypedMessage)
|
|
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxInvalidStreamInTypedMessage, messageDescription.MessageName)));
|
|
|
|
else if (isRequest)
|
|
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxInvalidStreamInRequest, operationName)));
|
|
|
|
else
|
|
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxInvalidStreamInResponse, operationName)));
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static bool HasStream(MessageDescription messageDescription)
|
|
|
|
{
|
|
|
|
if (messageDescription.Body.ReturnValue != null && messageDescription.Body.ReturnValue.Type == typeof(Stream))
|
|
|
|
return true;
|
|
|
|
foreach (MessagePartDescription part in messageDescription.Body.Parts)
|
|
|
|
{
|
|
|
|
if (part.Type == typeof(Stream))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static MessagePartDescription GetStreamPart(MessageDescription messageDescription)
|
|
|
|
{
|
|
|
|
if (OperationFormatter.IsValidReturnValue(messageDescription.Body.ReturnValue))
|
|
|
|
{
|
|
|
|
if (messageDescription.Body.Parts.Count == 0)
|
|
|
|
if (messageDescription.Body.ReturnValue.Type == typeof(Stream))
|
|
|
|
return messageDescription.Body.ReturnValue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (messageDescription.Body.Parts.Count == 1)
|
|
|
|
if (messageDescription.Body.Parts[0].Type == typeof(Stream))
|
|
|
|
return messageDescription.Body.Parts[0];
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal static bool IsStream(MessageDescription messageDescription)
|
|
|
|
{
|
|
|
|
return GetStreamPart(messageDescription) != null;
|
|
|
|
}
|
|
|
|
|
|
|
|
internal class MessageBodyStream : Stream
|
|
|
|
{
|
|
|
|
Message message;
|
|
|
|
XmlDictionaryReader reader;
|
|
|
|
long position;
|
|
|
|
string wrapperName, wrapperNs;
|
|
|
|
string elementName, elementNs;
|
|
|
|
bool isRequest;
|
|
|
|
internal MessageBodyStream(Message message, string wrapperName, string wrapperNs, string elementName, string elementNs, bool isRequest)
|
|
|
|
{
|
|
|
|
this.message = message;
|
|
|
|
this.position = 0;
|
|
|
|
this.wrapperName = wrapperName;
|
|
|
|
this.wrapperNs = wrapperNs;
|
|
|
|
this.elementName = elementName;
|
|
|
|
this.elementNs = elementNs;
|
|
|
|
this.isRequest = isRequest;
|
|
|
|
}
|
|
|
|
|
|
|
|
public override int Read(byte[] buffer, int offset, int count)
|
|
|
|
{
|
|
|
|
EnsureStreamIsOpen();
|
|
|
|
if (buffer == null)
|
|
|
|
throw TraceUtility.ThrowHelperError(new ArgumentNullException("buffer"), this.message);
|
|
|
|
if (offset < 0)
|
|
|
|
throw TraceUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", offset,
|
|
|
|
SR.GetString(SR.ValueMustBeNonNegative)), this.message);
|
|
|
|
if (count < 0)
|
|
|
|
throw TraceUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", count,
|
|
|
|
SR.GetString(SR.ValueMustBeNonNegative)), this.message);
|
|
|
|
if (buffer.Length - offset < count)
|
|
|
|
throw TraceUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.SFxInvalidStreamOffsetLength, offset + count)), this.message);
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
|
|
|
|
if (reader == null)
|
|
|
|
{
|
|
|
|
reader = message.GetReaderAtBodyContents();
|
|
|
|
if (wrapperName != null)
|
|
|
|
{
|
|
|
|
reader.MoveToContent();
|
|
|
|
reader.ReadStartElement(wrapperName, wrapperNs);
|
|
|
|
}
|
|
|
|
reader.MoveToContent();
|
|
|
|
if (reader.NodeType == XmlNodeType.EndElement)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
reader.ReadStartElement(elementName, elementNs);
|
|
|
|
}
|
|
|
|
if (reader.MoveToContent() != XmlNodeType.Text)
|
|
|
|
{
|
|
|
|
Exhaust(reader);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int bytesRead = reader.ReadContentAsBase64(buffer, offset, count);
|
|
|
|
position += bytesRead;
|
|
|
|
if (bytesRead == 0)
|
|
|
|
{
|
|
|
|
Exhaust(reader);
|
|
|
|
}
|
|
|
|
return bytesRead;
|
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
|
|
|
if (Fx.IsFatal(ex))
|
|
|
|
throw;
|
|
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new IOException(SR.GetString(SR.SFxStreamIOException), ex));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void EnsureStreamIsOpen()
|
|
|
|
{
|
|
|
|
if (message.State == MessageState.Closed)
|
|
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(SR.GetString(
|
|
|
|
isRequest ? SR.SFxStreamRequestMessageClosed : SR.SFxStreamResponseMessageClosed)));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void Exhaust(XmlDictionaryReader reader)
|
|
|
|
{
|
|
|
|
if (reader != null)
|
|
|
|
{
|
|
|
|
while (reader.Read())
|
|
|
|
{
|
|
|
|
// drain
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public override long Position
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
EnsureStreamIsOpen();
|
|
|
|
return position;
|
|
|
|
}
|
|
|
|
set { throw TraceUtility.ThrowHelperError(new NotSupportedException(), message); }
|
|
|
|
}
|
|
|
|
|
|
|
|
public override void Close()
|
|
|
|
{
|
|
|
|
message.Close();
|
|
|
|
if (reader != null)
|
|
|
|
{
|
|
|
|
reader.Close();
|
|
|
|
reader = null;
|
|
|
|
}
|
|
|
|
base.Close();
|
|
|
|
}
|
|
|
|
public override bool CanRead { get { return message.State != MessageState.Closed; } }
|
|
|
|
public override bool CanSeek { get { return false; } }
|
|
|
|
public override bool CanWrite { get { return false; } }
|
|
|
|
public override long Length
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
2017-08-21 15:34:15 +00:00
|
|
|
#pragma warning suppress 56503 // Microsoft, not a seekable stream, it is ok to throw NotSupported in this case
|
2016-08-03 10:59:49 +00:00
|
|
|
throw TraceUtility.ThrowHelperError(new NotSupportedException(), this.message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
public override void Flush() { throw TraceUtility.ThrowHelperError(new NotSupportedException(), this.message); }
|
|
|
|
public override long Seek(long offset, SeekOrigin origin) { throw TraceUtility.ThrowHelperError(new NotSupportedException(), this.message); }
|
|
|
|
public override void SetLength(long value) { throw TraceUtility.ThrowHelperError(new NotSupportedException(), this.message); }
|
|
|
|
public override void Write(byte[] buffer, int offset, int count) { throw TraceUtility.ThrowHelperError(new NotSupportedException(), this.message); }
|
|
|
|
}
|
|
|
|
|
|
|
|
class OperationStreamProvider : IStreamProvider
|
|
|
|
{
|
|
|
|
Stream stream;
|
|
|
|
|
|
|
|
internal OperationStreamProvider(Stream stream)
|
|
|
|
{
|
|
|
|
this.stream = stream;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Stream GetStream()
|
|
|
|
{
|
|
|
|
return stream;
|
|
|
|
}
|
|
|
|
public void ReleaseStream(Stream stream)
|
|
|
|
{
|
|
|
|
//Noop
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|