Empty XmlReader message caused failure on buffereed copy.
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel.Channels / MessageImpl.cs
1 //
2 // MessageImpl.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // Copyright (C) 2006 Novell, Inc.  http://www.novell.com
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 using System;
29 using System.Collections.Generic;
30 using System.Linq;
31 using System.Runtime.Serialization;
32 using System.Xml;
33 using System.IO;
34
35 namespace System.ServiceModel.Channels
36 {
37         internal class AttributeInfo
38         {
39                 public string Prefix, Name, Namespace, Value;
40         }
41         
42         internal class AttributeCollection : List<AttributeInfo>
43         {
44                 public AttributeCollection ()
45                 {
46                 }
47
48                 public AttributeCollection (AttributeCollection copy)
49                         : base (copy)
50                 {
51                 }
52         }
53
54         internal class XmlReaderMessage : Message
55         {
56                 MessageVersion version;
57                 XmlDictionaryReader reader;
58                 MessageHeaders headers;
59                 MessageProperties properties = new MessageProperties ();
60                 bool is_empty, is_fault, body_started, body_consumed;
61                 int max_headers;
62                 AttributeCollection attributes;
63
64                 public XmlReaderMessage (MessageVersion version, XmlDictionaryReader reader, int maxSizeOfHeaders)
65                 {
66                         this.version = version;
67                         this.reader = reader;
68                         this.max_headers = maxSizeOfHeaders;
69
70                         ReadEnvelopeStart ();
71                 }
72
73                 public override MessageHeaders Headers {
74                         get {
75                                 if (headers == null)
76                                         ReadHeaders ();
77                                 return headers;
78                         }
79                 }
80
81                 public override bool IsEmpty {
82                         get {
83                                 ReadBodyStart ();
84                                 return is_empty;
85                         }
86                 }
87
88                 public override bool IsFault {
89                         get {
90                                 ReadBodyStart ();
91                                 return is_fault;
92                         }
93                 }
94
95                 public override MessageProperties Properties {
96                         get { return properties; }
97                 }
98
99                 public override MessageVersion Version {
100                         get { return version; }
101                 }
102
103                 protected override MessageBuffer OnCreateBufferedCopy (
104                         int maxBufferSize)
105                 {
106                         ReadBodyStart ();
107                         var headers = new MessageHeaders (Headers);
108                         var props = new MessageProperties (Properties);
109                         if (IsEmpty)
110                                 return new DefaultMessageBuffer (headers, props, attributes);
111                         else
112                                 return new DefaultMessageBuffer (maxBufferSize, headers, props, new XmlReaderBodyWriter (reader), IsFault, attributes);
113                 }
114
115                 protected override string OnGetBodyAttribute (
116                         string localName, string ns)
117                 {
118                         ReadBodyStart ();
119                         var att = attributes.FirstOrDefault (a => a.Name == localName && a.Namespace == ns);
120                         return att != null ? att.Value : null;
121                 }
122
123                 protected override XmlDictionaryReader OnGetReaderAtBodyContents ()
124                 {
125                         if (reader.ReadState == ReadState.Closed)
126                                 return reader; // silly, but that's what our test expects.
127                         ReadBodyStart ();
128                         if (is_empty)
129                                 throw new InvalidOperationException ("The message body is empty.");
130                         body_consumed = true;
131                         return reader;
132                 }
133
134                 protected override void OnWriteBodyContents (
135                         XmlDictionaryWriter writer)
136                 {
137                         XmlDictionaryReader reader = GetReaderAtBodyContents ();
138                         while (!reader.EOF && reader.NodeType != XmlNodeType.EndElement)
139                                 writer.WriteNode (reader, false);
140                 }
141
142                 static readonly char [] whitespaceChars = new char [] {' ', '\t', '\r', '\n'};
143
144                 void ReadEnvelopeStart ()
145                 {
146                         reader.MoveToContent ();
147                         if (reader.IsEmptyElement)
148                                 throw new ArgumentException ("Missing message content XML.");
149                         reader.ReadStartElement ("Envelope", Version.Envelope.Namespace);
150
151                         // SOAP Header
152                         reader.MoveToContent ();
153                 }
154
155                 void ReadHeaders ()
156                 {
157                         if (headers != null)
158                                 throw new InvalidOperationException ("XmlReader at headers is already consumed.");
159
160                         string envNS = Version.Envelope.Namespace;
161
162                         headers = new MessageHeaders (version);
163                         if (reader.LocalName != "Header" || reader.NamespaceURI != envNS)
164                                 return;
165
166                         bool isEmptyHeader = reader.IsEmptyElement;
167                         reader.ReadStartElement ("Header", envNS);
168                         reader.MoveToContent ();
169                         if (isEmptyHeader)
170                                 return;
171
172                         int nHeaders = 0;
173                         while (!reader.EOF && reader.NodeType != XmlNodeType.EndElement) {
174                                 if (reader.NodeType == XmlNodeType.Element) {
175                                         if (nHeaders++ == max_headers)
176                                                 throw new InvalidOperationException (String.Format ("Message header size has exceeded the maximum header size {0}", max_headers));
177                                         headers.Add (new MessageHeader.XmlMessageHeader (reader, Version));
178                                 }
179                                 else
180                                         reader.Skip ();
181                                 // FIXME: handle UnderstoodHeaders as well.
182                                 reader.MoveToContent ();
183                         }
184                         reader.ReadEndElement ();
185                         reader.MoveToContent ();
186                 }
187
188                 void ReadBodyStart ()
189                 {
190                         if (body_consumed)
191                                 throw new InvalidOperationException ("The message body XmlReader is already consumed.");
192
193                         if (body_started)
194                                 return;
195
196                         // read headers in advance.
197                         if (headers == null)
198                                 ReadHeaders ();
199
200                         // SOAP Body
201                         body_started = true;
202                         is_empty = reader.IsEmptyElement;
203                         attributes = new AttributeCollection ();
204                         reader.MoveToContent ();
205                         if (reader.MoveToFirstAttribute ()) {
206                                 do {
207                                         attributes.Add (new AttributeInfo () { Prefix = reader.Prefix, Name = reader.LocalName, Namespace = reader.NamespaceURI, Value = reader.Value});
208                                 } while (reader.MoveToNextAttribute ());
209                                 reader.MoveToElement ();
210                         }
211                         reader.ReadStartElement ("Body", Version.Envelope.Namespace);
212                         if (reader.NodeType == XmlNodeType.EndElement) {
213                                 is_empty = true;
214                                 reader.Read ();
215                         } else {
216                                 reader.MoveToContent ();
217                                 if (reader.NodeType == XmlNodeType.Element &&
218                                     reader.LocalName == "Fault" &&
219                                     reader.NamespaceURI == Version.Envelope.Namespace)
220                                         is_fault = true;
221                         }
222                 }
223
224                 protected override void OnWriteStartBody (
225                         XmlDictionaryWriter writer)
226                 {
227                         ReadBodyStart (); // consume up to attributes
228
229                         base.OnWriteStartBody (writer);
230                         foreach (var p in attributes)
231                                 writer.WriteAttributeString (p.Prefix, p.Name, p.Namespace, p.Value);
232                 }
233         }
234
235         internal abstract class MessageImplBase : Message
236         {
237                 MessageHeaders headers;
238                 MessageProperties properties = new MessageProperties ();
239                 AttributeCollection attributes;
240
241                 public MessageImplBase (MessageVersion version, string action, AttributeCollection attributes)
242                 {
243                         headers = new MessageHeaders (version);
244                         if (action != null)
245                                 headers.Action = action;
246                         this.attributes = attributes;
247                 }
248
249                 internal AttributeCollection Attributes {
250                         get { return attributes; }
251                 }
252
253                 public override MessageHeaders Headers {
254                         get { return headers; }
255                 }
256
257                 public override MessageProperties Properties {
258                         get { return properties; }
259                 }
260
261                 public override MessageVersion Version {
262                         get { return Headers.MessageVersion; }
263                 }
264
265                 protected override string OnGetBodyAttribute (
266                         string localName, string ns)
267                 {
268                         var att = attributes.FirstOrDefault (a => a.Name == localName && a.Namespace == ns);
269                         return att != null ? att.Value : null;
270                 }
271
272                 protected override void OnWriteStartBody (
273                         XmlDictionaryWriter writer)
274                 {
275                         var dic = Constants.SoapDictionary;
276                         writer.WriteStartElement ("s", dic.Add ("Body"), dic.Add (Version.Envelope.Namespace));
277                         foreach (var p in Attributes)
278                                 writer.WriteAttributeString (p.Prefix, p.Name, p.Namespace, p.Value);
279                 }
280         }
281
282         internal class EmptyMessage : MessageImplBase
283         {
284                 static readonly AttributeCollection empty_attributes = new AttributeCollection ();
285
286                 public EmptyMessage (MessageVersion version, string action)
287                         : base (version, action, empty_attributes)
288                 {
289                 }
290
291                 public override bool IsEmpty {
292                         get { return true; }
293                 }
294
295                 protected override void OnWriteBodyContents (
296                         XmlDictionaryWriter writer)
297                 {
298                 }
299
300                 protected override MessageBuffer OnCreateBufferedCopy (
301                         int maxBufferSize)
302                 {
303                         return new DefaultMessageBuffer (Headers, Properties, Attributes);
304                 }
305         }
306
307         internal class SimpleMessage : MessageImplBase
308         {
309                 BodyWriter body;
310                 bool is_fault;
311
312                 public SimpleMessage (MessageVersion version,
313                         string action, BodyWriter body, bool isFault, AttributeCollection attributes)
314                         : base (version, action, attributes)
315                 {
316                         this.body = body;
317                         this.is_fault = isFault;
318                 }
319
320                 public override bool IsEmpty {
321                         get { return false; }
322                 }
323
324                 public override bool IsFault {
325                         get { return is_fault; }
326                 }
327
328                 protected override void OnWriteBodyContents (
329                         XmlDictionaryWriter writer)
330                 {
331                         body.WriteBodyContents (writer);
332                 }
333
334                 protected override MessageBuffer OnCreateBufferedCopy (
335                         int maxBufferSize)
336                 {
337                         var headers = new MessageHeaders (Headers);
338                         var props = new MessageProperties (Properties);
339                         var atts = new AttributeCollection (Attributes);
340                         return new DefaultMessageBuffer (maxBufferSize, headers, props, body.CreateBufferedCopy (maxBufferSize), IsFault, atts);
341                 }
342         }
343 }
344