2009-05-22 Atsushi Enomoto <atsushi@ximian.com>
[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.Runtime.Serialization;
30 using System.Xml;
31 using System.IO;
32
33 namespace System.ServiceModel.Channels
34 {
35         internal class XmlReaderMessage : Message
36         {
37                 MessageVersion version;
38                 XmlDictionaryReader reader;
39                 MessageHeaders headers;
40                 MessageProperties properties = new MessageProperties ();
41                 bool is_empty, is_fault, body_started, body_consumed;
42                 int max_headers;
43
44                 public XmlReaderMessage (MessageVersion version, XmlDictionaryReader reader, int maxSizeOfHeaders)
45                 {
46                         this.version = version;
47                         this.reader = reader;
48                         this.max_headers = maxSizeOfHeaders;
49
50                         ReadEnvelopeStart ();
51                 }
52
53                 public override MessageHeaders Headers {
54                         get {
55                                 if (headers == null)
56                                         ReadHeaders ();
57                                 return headers;
58                         }
59                 }
60
61                 public override bool IsEmpty {
62                         get {
63                                 if (!body_started)
64                                         ReadBodyStart ();
65                                 return is_empty;
66                         }
67                 }
68
69                 public override bool IsFault {
70                         get {
71                                 if (!body_started)
72                                         ReadBodyStart ();
73                                 return is_fault;
74                         }
75                 }
76
77                 public override MessageProperties Properties {
78                         get { return properties; }
79                 }
80
81                 public override MessageVersion Version {
82                         get { return version; }
83                 }
84
85                 protected override string OnGetBodyAttribute (
86                         string localName, string ns)
87                 {
88                         if (headers == null)
89                                 ReadHeaders ();
90                         return reader.GetAttribute (localName, ns);
91                 }
92
93                 protected override XmlDictionaryReader OnGetReaderAtBodyContents ()
94                 {
95                         if (reader.ReadState == ReadState.Closed)
96                                 return reader; // silly, but that's what our test expects.
97                         if (body_consumed)
98                                 throw new InvalidOperationException ("The message body XmlReader is already consumed.");
99                         if (!body_started)
100                                 ReadBodyStart ();
101                         if (is_empty)
102                                 throw new InvalidOperationException ("The message body is empty.");
103                         body_consumed = true;
104                         return reader;
105                 }
106
107                 protected override void OnWriteBodyContents (
108                         XmlDictionaryWriter writer)
109                 {
110                         XmlDictionaryReader reader = GetReaderAtBodyContents ();
111                         while (!reader.EOF && reader.NodeType != XmlNodeType.EndElement)
112                                 writer.WriteNode (reader, false);
113                 }
114
115                 static readonly char [] whitespaceChars = new char [] {' ', '\t', '\r', '\n'};
116
117                 void ReadEnvelopeStart ()
118                 {
119                         reader.MoveToContent ();
120                         if (reader.IsEmptyElement)
121                                 throw new ArgumentException ("Missing message content XML.");
122                         reader.ReadStartElement ("Envelope", Version.Envelope.Namespace);
123
124                         // SOAP Header
125                         reader.MoveToContent ();
126                 }
127
128                 void ReadHeaders ()
129                 {
130                         if (headers != null)
131                                 throw new InvalidOperationException ("XmlReader at headers is already consumed.");
132
133                         string envNS = Version.Envelope.Namespace;
134
135                         headers = new MessageHeaders (version, max_headers);
136                         if (reader.LocalName != "Header" || reader.NamespaceURI != envNS)
137                                 return;
138
139                         bool isEmptyHeader = reader.IsEmptyElement;
140                         reader.ReadStartElement ("Header", envNS);
141                         reader.MoveToContent ();
142                         if (isEmptyHeader)
143                                 return;
144
145                         while (!reader.EOF && reader.NodeType != XmlNodeType.EndElement) {
146                                 if (reader.NodeType == XmlNodeType.Element)
147                                         headers.Add (new MessageHeader.RawMessageHeader (reader, envNS));
148                                 else
149                                         reader.Skip ();
150                                 // FIXME: handle UnderstoodHeaders as well.
151                                 reader.MoveToContent ();
152                         }
153                         reader.ReadEndElement ();
154                         reader.MoveToContent ();
155                 }
156
157                 void ReadBodyStart ()
158                 {
159                         // read headers in advance.
160                         if (headers == null)
161                                 ReadHeaders ();
162
163                         // SOAP Body
164                         body_started = true;
165                         is_empty = reader.IsEmptyElement;
166                         if (reader.MoveToAttribute ("Id", Constants.WsuNamespace)) {
167                                 BodyId = reader.Value;
168                                 reader.MoveToElement ();
169                         }
170                         reader.ReadStartElement ("Body", Version.Envelope.Namespace);
171                         if (reader.NodeType == XmlNodeType.EndElement) {
172                                 is_empty = true;
173                                 reader.Read ();
174                         } else {
175                                 reader.MoveToContent ();
176                                 if (reader.NodeType == XmlNodeType.Element &&
177                                     reader.LocalName == "Fault" &&
178                                     reader.NamespaceURI == Version.Envelope.Namespace)
179                                         is_fault = true;
180                         }
181                 }
182         }
183
184         internal abstract class MessageImplBase : Message
185         {
186                 MessageHeaders headers;
187                 MessageProperties properties = new MessageProperties ();
188
189                 public MessageImplBase (MessageVersion version, string action)
190                 {
191                         headers = new MessageHeaders (version);
192                         if (action != null)
193                                 headers.Action = action;
194                 }
195
196                 public override MessageHeaders Headers {
197                         get { return headers; }
198                 }
199
200                 public override MessageProperties Properties {
201                         get { return properties; }
202                 }
203
204                 public override MessageVersion Version {
205                         get { return Headers.MessageVersion; }
206                 }
207         }
208
209         internal class EmptyMessage : MessageImplBase
210         {
211                 public EmptyMessage (MessageVersion version, string action)
212                         : base (version, action)
213                 {
214                 }
215
216                 public override bool IsEmpty {
217                         get { return true; }
218                 }
219
220                 protected override void OnWriteBodyContents (
221                         XmlDictionaryWriter writer)
222                 {
223                 }
224
225                 protected override MessageBuffer OnCreateBufferedCopy (
226                         int maxBufferSize)
227                 {
228                         return new DefaultMessageBuffer (Headers, Properties);
229                 }
230         }
231
232         internal class SimpleMessage : MessageImplBase
233         {
234                 BodyWriter body;
235                 bool is_fault;
236
237                 public SimpleMessage (MessageVersion version,
238                         string action, BodyWriter body, bool isFault)
239                         : base (version, action)
240                 {
241                         this.body = body;
242                         this.is_fault = isFault;
243                 }
244
245                 public override bool IsEmpty {
246                         get { return false; }
247                 }
248
249                 public override bool IsFault {
250                         get { return is_fault; }
251                 }
252
253                 protected override void OnWriteBodyContents (
254                         XmlDictionaryWriter writer)
255                 {
256                         body.WriteBodyContents (writer);
257                 }
258         }
259 }
260