1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //-----------------------------------------------------------------------------
5 namespace System.ServiceModel.Dispatcher
9 using System.ServiceModel;
10 using System.ServiceModel.Channels;
11 using System.ServiceModel.Description;
12 using System.ServiceModel.Diagnostics;
24 const int returnValueIndex = -1;
26 internal static StreamFormatter Create(MessageDescription messageDescription, string operationName, bool isRequest)
28 MessagePartDescription streamPart = ValidateAndGetStreamPart(messageDescription, isRequest, operationName);
29 if (streamPart == null)
31 return new StreamFormatter(messageDescription, streamPart, operationName, isRequest);
34 StreamFormatter(MessageDescription messageDescription, MessagePartDescription streamPart, string operationName, bool isRequest)
36 if ((object)streamPart == (object)messageDescription.Body.ReturnValue)
37 this.streamIndex = returnValueIndex;
39 this.streamIndex = streamPart.Index;
40 wrapperName = messageDescription.Body.WrapperName;
41 wrapperNS = messageDescription.Body.WrapperNamespace;
42 partName = streamPart.Name;
43 partNS = streamPart.Namespace;
44 this.isRequest = isRequest;
45 this.operationName = operationName;
48 internal void Serialize(XmlDictionaryWriter writer, object[] parameters, object returnValue)
50 Stream streamValue = GetStreamAndWriteStartWrapperIfNecessary(writer, parameters, returnValue);
51 writer.WriteValue(new OperationStreamProvider(streamValue));
52 WriteEndWrapperIfNecessary(writer);
55 Stream GetStreamAndWriteStartWrapperIfNecessary(XmlDictionaryWriter writer, object[] parameters, object returnValue)
57 Stream streamValue = GetStreamValue(parameters, returnValue);
58 if (streamValue == null)
59 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(partName);
60 if (WrapperName != null)
61 writer.WriteStartElement(WrapperName, WrapperNamespace);
62 writer.WriteStartElement(PartName, PartNamespace);
66 void WriteEndWrapperIfNecessary(XmlDictionaryWriter writer)
68 writer.WriteEndElement();
69 if (wrapperName != null)
70 writer.WriteEndElement();
73 internal IAsyncResult BeginSerialize(XmlDictionaryWriter writer, object[] parameters, object returnValue, AsyncCallback callback, object state)
75 return new SerializeAsyncResult(this, writer, parameters, returnValue, callback, state);
78 public void EndSerialize(IAsyncResult result)
80 SerializeAsyncResult.End(result);
83 class SerializeAsyncResult : AsyncResult
85 static AsyncCompletion handleEndSerialize = new AsyncCompletion(HandleEndSerialize);
87 StreamFormatter streamFormatter;
88 XmlDictionaryWriter writer;
90 internal SerializeAsyncResult(StreamFormatter streamFormatter, XmlDictionaryWriter writer, object[] parameters, object returnValue,
91 AsyncCallback callback, object state)
92 : base(callback, state)
94 this.streamFormatter = streamFormatter;
96 bool completeSelf = true;
98 Stream streamValue = streamFormatter.GetStreamAndWriteStartWrapperIfNecessary(writer, parameters, returnValue);
99 IAsyncResult result = writer.WriteValueAsync(new OperationStreamProvider(streamValue)).AsAsyncResult(PrepareAsyncCompletion(handleEndSerialize), this);
100 completeSelf = SyncContinue(result);
102 // Note: The current task implementation hard codes the "IAsyncResult.CompletedSynchronously" property to false, so this fast path will never
103 // be hit, and we will always hop threads. CSDMain #210220
110 static bool HandleEndSerialize(IAsyncResult result)
112 SerializeAsyncResult thisPtr = (SerializeAsyncResult)result.AsyncState;
113 thisPtr.streamFormatter.WriteEndWrapperIfNecessary(thisPtr.writer);
117 public static void End(IAsyncResult result)
119 AsyncResult.End<SerializeAsyncResult>(result);
123 internal void Deserialize(object[] parameters, ref object retVal, Message message)
125 SetStreamValue(parameters, ref retVal, new MessageBodyStream(message, WrapperName, WrapperNamespace, PartName, PartNamespace, isRequest));
128 internal string WrapperName
130 get { return wrapperName; }
131 set { wrapperName = value; }
134 internal string WrapperNamespace
136 get { return wrapperNS; }
137 set { wrapperNS = value; }
140 internal string PartName
142 get { return partName; }
145 internal string PartNamespace
147 get { return partNS; }
151 Stream GetStreamValue(object[] parameters, object returnValue)
153 if (streamIndex == returnValueIndex)
154 return (Stream)returnValue;
155 return (Stream)parameters[streamIndex];
158 void SetStreamValue(object[] parameters, ref object returnValue, Stream streamValue)
160 if (streamIndex == returnValueIndex)
161 returnValue = streamValue;
163 parameters[streamIndex] = streamValue;
166 static MessagePartDescription ValidateAndGetStreamPart(MessageDescription messageDescription, bool isRequest, string operationName)
168 MessagePartDescription part = GetStreamPart(messageDescription);
171 if (HasStream(messageDescription))
173 if (messageDescription.IsTypedMessage)
174 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxInvalidStreamInTypedMessage, messageDescription.MessageName)));
176 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxInvalidStreamInRequest, operationName)));
178 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxInvalidStreamInResponse, operationName)));
183 private static bool HasStream(MessageDescription messageDescription)
185 if (messageDescription.Body.ReturnValue != null && messageDescription.Body.ReturnValue.Type == typeof(Stream))
187 foreach (MessagePartDescription part in messageDescription.Body.Parts)
189 if (part.Type == typeof(Stream))
195 static MessagePartDescription GetStreamPart(MessageDescription messageDescription)
197 if (OperationFormatter.IsValidReturnValue(messageDescription.Body.ReturnValue))
199 if (messageDescription.Body.Parts.Count == 0)
200 if (messageDescription.Body.ReturnValue.Type == typeof(Stream))
201 return messageDescription.Body.ReturnValue;
205 if (messageDescription.Body.Parts.Count == 1)
206 if (messageDescription.Body.Parts[0].Type == typeof(Stream))
207 return messageDescription.Body.Parts[0];
212 internal static bool IsStream(MessageDescription messageDescription)
214 return GetStreamPart(messageDescription) != null;
217 internal class MessageBodyStream : Stream
220 XmlDictionaryReader reader;
222 string wrapperName, wrapperNs;
223 string elementName, elementNs;
225 internal MessageBodyStream(Message message, string wrapperName, string wrapperNs, string elementName, string elementNs, bool isRequest)
227 this.message = message;
229 this.wrapperName = wrapperName;
230 this.wrapperNs = wrapperNs;
231 this.elementName = elementName;
232 this.elementNs = elementNs;
233 this.isRequest = isRequest;
236 public override int Read(byte[] buffer, int offset, int count)
238 EnsureStreamIsOpen();
240 throw TraceUtility.ThrowHelperError(new ArgumentNullException("buffer"), this.message);
242 throw TraceUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", offset,
243 SR.GetString(SR.ValueMustBeNonNegative)), this.message);
245 throw TraceUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", count,
246 SR.GetString(SR.ValueMustBeNonNegative)), this.message);
247 if (buffer.Length - offset < count)
248 throw TraceUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.SFxInvalidStreamOffsetLength, offset + count)), this.message);
255 reader = message.GetReaderAtBodyContents();
256 if (wrapperName != null)
258 reader.MoveToContent();
259 reader.ReadStartElement(wrapperName, wrapperNs);
261 reader.MoveToContent();
262 if (reader.NodeType == XmlNodeType.EndElement)
267 reader.ReadStartElement(elementName, elementNs);
269 if (reader.MoveToContent() != XmlNodeType.Text)
274 int bytesRead = reader.ReadContentAsBase64(buffer, offset, count);
275 position += bytesRead;
286 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new IOException(SR.GetString(SR.SFxStreamIOException), ex));
290 private void EnsureStreamIsOpen()
292 if (message.State == MessageState.Closed)
293 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(SR.GetString(
294 isRequest ? SR.SFxStreamRequestMessageClosed : SR.SFxStreamResponseMessageClosed)));
297 static void Exhaust(XmlDictionaryReader reader)
301 while (reader.Read())
308 public override long Position
312 EnsureStreamIsOpen();
315 set { throw TraceUtility.ThrowHelperError(new NotSupportedException(), message); }
318 public override void Close()
328 public override bool CanRead { get { return message.State != MessageState.Closed; } }
329 public override bool CanSeek { get { return false; } }
330 public override bool CanWrite { get { return false; } }
331 public override long Length
335 #pragma warning suppress 56503 // Microsoft, not a seekable stream, it is ok to throw NotSupported in this case
336 throw TraceUtility.ThrowHelperError(new NotSupportedException(), this.message);
339 public override void Flush() { throw TraceUtility.ThrowHelperError(new NotSupportedException(), this.message); }
340 public override long Seek(long offset, SeekOrigin origin) { throw TraceUtility.ThrowHelperError(new NotSupportedException(), this.message); }
341 public override void SetLength(long value) { throw TraceUtility.ThrowHelperError(new NotSupportedException(), this.message); }
342 public override void Write(byte[] buffer, int offset, int count) { throw TraceUtility.ThrowHelperError(new NotSupportedException(), this.message); }
345 class OperationStreamProvider : IStreamProvider
349 internal OperationStreamProvider(Stream stream)
351 this.stream = stream;
354 public Stream GetStream()
358 public void ReleaseStream(Stream stream)