1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4 namespace System.ServiceModel.Channels
5 {
6     using System.Xml;
7     using System.ServiceModel;
8     using System.ServiceModel.Dispatcher;
9     using System.Xml.XPath;
10     using System.Diagnostics;
11     using System.IO;
12     using System.Collections.Generic;
13 
14     public abstract class MessageBuffer : IXPathNavigable, IDisposable
15     {
16         public abstract int BufferSize { get; }
17 
IDisposable.Dispose()18         void IDisposable.Dispose()
19         {
20             Close();
21         }
22 
Close()23         public abstract void Close();
24 
WriteMessage(Stream stream)25         public virtual void WriteMessage(Stream stream)
26         {
27             if (stream == null)
28                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("stream"));
29             Message message = CreateMessage();
30             using (message)
31             {
32                 XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(stream, XD.Dictionary, null, false);
33                 using (writer)
34                 {
35                     message.WriteMessage(writer);
36                 }
37             }
38         }
39 
40         public virtual string MessageContentType
41         {
42             get { return FramingEncodingString.Binary; }
43         }
44 
CreateMessage()45         public abstract Message CreateMessage();
46 
CreateBufferDisposedException()47         internal Exception CreateBufferDisposedException()
48         {
49             return new ObjectDisposedException("", SR.GetString(SR.MessageBufferIsClosed));
50         }
51 
CreateNavigator()52         public XPathNavigator CreateNavigator()
53         {
54             return CreateNavigator(int.MaxValue, XmlSpace.None);
55         }
56 
CreateNavigator(int nodeQuota)57         public XPathNavigator CreateNavigator(int nodeQuota)
58         {
59             return CreateNavigator(nodeQuota, XmlSpace.None);
60         }
61 
CreateNavigator(XmlSpace space)62         public XPathNavigator CreateNavigator(XmlSpace space)
63         {
64             return CreateNavigator(int.MaxValue, space);
65         }
66 
CreateNavigator(int nodeQuota, XmlSpace space)67         public XPathNavigator CreateNavigator(int nodeQuota, XmlSpace space)
68         {
69             if (nodeQuota <= 0)
70                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("nodeQuota", SR.GetString(SR.FilterQuotaRange)));
71 
72             return new SeekableMessageNavigator(this.CreateMessage(), nodeQuota, space, true, true);
73         }
74     }
75 
76     class DefaultMessageBuffer : MessageBuffer
77     {
78         XmlBuffer msgBuffer;
79         KeyValuePair<string, object>[] properties;
80         bool[] understoodHeaders;
81         bool closed;
82         MessageVersion version;
83         Uri to;
84         string action;
85         bool isNullMessage;
86 
DefaultMessageBuffer(Message message, XmlBuffer msgBuffer)87         public DefaultMessageBuffer(Message message, XmlBuffer msgBuffer)
88         {
89             this.msgBuffer = msgBuffer;
90             this.version = message.Version;
91             this.isNullMessage = message is NullMessage;
92 
93             properties = new KeyValuePair<string, object>[message.Properties.Count];
94             ((ICollection<KeyValuePair<string, object>>)message.Properties).CopyTo(properties, 0);
95             understoodHeaders = new bool[message.Headers.Count];
96             for (int i = 0; i < understoodHeaders.Length; ++i)
97                 understoodHeaders[i] = message.Headers.IsUnderstood(i);
98 
99             //CSDMain 17837: CreateBufferedCopy should have code to copy over the To and Action headers
100             if (version == MessageVersion.None)
101             {
102                 this.to = message.Headers.To;
103                 this.action = message.Headers.Action;
104             }
105         }
106 
107         object ThisLock
108         {
109             get { return msgBuffer; }
110         }
111 
112         public override int BufferSize
113         {
114             get { return msgBuffer.BufferSize; }
115         }
116 
Close()117         public override void Close()
118         {
119             lock (ThisLock)
120             {
121                 if (closed)
122                     return;
123 
124                 closed = true;
125                 for (int i = 0; i < this.properties.Length; i++)
126                 {
127                     IDisposable disposable = this.properties[i].Value as IDisposable;
128                     if (disposable != null)
129                         disposable.Dispose();
130                 }
131             }
132         }
133 
CreateMessage()134         public override Message CreateMessage()
135         {
136             if (closed)
137                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateBufferDisposedException());
138 
139             Message msg;
140             if (this.isNullMessage)
141             {
142                 msg = new NullMessage();
143             }
144             else
145             {
146                 msg = Message.CreateMessage(msgBuffer.GetReader(0), int.MaxValue, this.version);
147             }
148 
149             lock (ThisLock)
150             {
151                 msg.Properties.CopyProperties(properties);
152             }
153 
154             for (int i = 0; i < understoodHeaders.Length; ++i)
155             {
156                 if (understoodHeaders[i])
157                     msg.Headers.AddUnderstood(i);
158             }
159 
160             if (this.to != null)
161             {
162                 msg.Headers.To = this.to;
163             }
164 
165             if (this.action != null)
166             {
167                 msg.Headers.Action = this.action;
168             }
169 
170             return msg;
171         }
172     }
173 
174     class BufferedMessageBuffer : MessageBuffer
175     {
176         IBufferedMessageData messageData;
177         KeyValuePair<string, object>[] properties;
178         bool closed;
179         object thisLock = new object();
180         bool[] understoodHeaders;
181         bool understoodHeadersModified;
182 
BufferedMessageBuffer(IBufferedMessageData messageData, KeyValuePair<string, object>[] properties, bool[] understoodHeaders, bool understoodHeadersModified)183         public BufferedMessageBuffer(IBufferedMessageData messageData,
184             KeyValuePair<string, object>[] properties, bool[] understoodHeaders, bool understoodHeadersModified)
185         {
186             this.messageData = messageData;
187             this.properties = properties;
188             this.understoodHeaders = understoodHeaders;
189             this.understoodHeadersModified = understoodHeadersModified;
190             messageData.Open();
191         }
192 
193         public override int BufferSize
194         {
195             get
196             {
197                 lock (ThisLock)
198                 {
199                     if (closed)
200 #pragma warning suppress 56503 // Microsoft, Invalid State after dispose
201                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateBufferDisposedException());
202                     return messageData.Buffer.Count;
203                 }
204             }
205         }
206 
WriteMessage(Stream stream)207         public override void WriteMessage(Stream stream)
208         {
209             if (stream == null)
210                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("stream"));
211             lock (ThisLock)
212             {
213                 if (closed)
214                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateBufferDisposedException());
215                 ArraySegment<byte> buffer = messageData.Buffer;
216                 stream.Write(buffer.Array, buffer.Offset, buffer.Count);
217             }
218         }
219 
220         public override string MessageContentType
221         {
222             get
223             {
224                 lock (ThisLock)
225                 {
226                     if (closed)
227 #pragma warning suppress 56503 // Microsoft, Invalid State after dispose
228                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateBufferDisposedException());
229                     return messageData.MessageEncoder.ContentType;
230                 }
231             }
232         }
233 
234         object ThisLock
235         {
236             get { return thisLock; }
237         }
238 
Close()239         public override void Close()
240         {
241             lock (ThisLock)
242             {
243                 if (!closed)
244                 {
245                     closed = true;
246                     messageData.Close();
247                     messageData = null;
248                 }
249             }
250         }
251 
CreateMessage()252         public override Message CreateMessage()
253         {
254             lock (ThisLock)
255             {
256                 if (closed)
257                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateBufferDisposedException());
258                 RecycledMessageState recycledMessageState = messageData.TakeMessageState();
259                 if (recycledMessageState == null)
260                     recycledMessageState = new RecycledMessageState();
261                 BufferedMessage bufferedMessage = new BufferedMessage(messageData, recycledMessageState, this.understoodHeaders, this.understoodHeadersModified);
262                 bufferedMessage.Properties.CopyProperties(this.properties);
263                 messageData.Open();
264                 return bufferedMessage;
265             }
266         }
267     }
268 
269     class BodyWriterMessageBuffer : MessageBuffer
270     {
271         BodyWriter bodyWriter;
272         KeyValuePair<string, object>[] properties;
273         MessageHeaders headers;
274         bool closed;
275         object thisLock = new object();
276 
BodyWriterMessageBuffer(MessageHeaders headers, KeyValuePair<string, object>[] properties, BodyWriter bodyWriter)277         public BodyWriterMessageBuffer(MessageHeaders headers,
278             KeyValuePair<string, object>[] properties, BodyWriter bodyWriter)
279         {
280             this.bodyWriter = bodyWriter;
281             this.headers = new MessageHeaders(headers);
282             this.properties = properties;
283         }
284 
285         protected object ThisLock
286         {
287             get { return thisLock; }
288         }
289 
290         public override int BufferSize
291         {
292             get { return 0; }
293         }
294 
Close()295         public override void Close()
296         {
297             lock (ThisLock)
298             {
299                 if (!closed)
300                 {
301                     closed = true;
302                     bodyWriter = null;
303                     headers = null;
304                     properties = null;
305                 }
306             }
307         }
308 
CreateMessage()309         public override Message CreateMessage()
310         {
311             lock (ThisLock)
312             {
313                 if (closed)
314                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateBufferDisposedException());
315                 return new BodyWriterMessage(headers, properties, bodyWriter);
316             }
317         }
318 
319         protected BodyWriter BodyWriter
320         {
321             get { return bodyWriter; }
322         }
323 
324         protected MessageHeaders Headers
325         {
326             get { return headers; }
327         }
328 
329         protected KeyValuePair<string, object>[] Properties
330         {
331             get { return properties; }
332         }
333 
334         protected bool Closed
335         {
336             get { return closed; }
337         }
338     }
339 }
340