refactoring: making fields readonly
[mono.git] / mcs / class / System.Web.Services / System.Web.Services.Protocols / WebServiceHelper.cs
1 //
2 // System.Web.Services.Protocols.WebServiceHelper.cs
3 //
4 // Author:
5 //   Lluis Sanchez Gual (lluis@ximian.com)
6 //
7 // Copyright (C) Ximian, Inc. 2003
8 //
9
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System;
32 using System.IO;
33 using System.Net;
34 using System.Text;
35 using System.Xml;
36 using System.Xml.Schema;
37 using System.Xml.Serialization;
38 using System.Web.Services.Description;
39
40 namespace System.Web.Services.Protocols
41 {
42         internal class WebServiceHelper
43         {
44                 public const string SoapEnvelopeNamespace = "http://schemas.xmlsoap.org/soap/envelope/";
45                 public const string Soap12EnvelopeNamespace = "http://www.w3.org/2003/05/soap-envelope";
46                 public const string SoapEncodingNamespace = "http://schemas.xmlsoap.org/soap/encoding/";
47                 public const string Soap12EncodingNamespace = "http://www.w3.org/2003/05/soap-encoding";
48                 static readonly char [] trimChars = { '"', '\'' };
49                 static readonly bool prettyXml;
50                 
51                 static WebServiceHelper ()
52                 {
53                         string pxml = Environment.GetEnvironmentVariable ("MONO_WEBSERVICES_PRETTYXML");
54                         prettyXml = (pxml != null && pxml != "no");
55                 }
56                 
57                 public static XmlTextWriter CreateXmlWriter (Stream s)
58                 {
59                         // What a waste of UTF8encoders, but it has to be thread safe.
60                         XmlTextWriter xtw = new XmlTextWriter (s, new UTF8Encoding (false));
61                                 
62                         if (prettyXml)
63                                 xtw.Formatting = Formatting.Indented;
64                                 
65                         return xtw;
66                 }
67                 
68                 public static Encoding GetContentEncoding (string cts, out string content_type)
69                 {
70                         string encoding;
71
72                         if (cts == null) cts = "";
73                         
74                         encoding = "utf-8";
75                         int start = 0;
76                         int idx = cts.IndexOf (';');
77                         if (idx == -1)
78                                 content_type = cts;
79                         else
80                                 content_type = cts.Substring (0, idx);
81
82                         content_type = content_type.Trim ();
83                         for (start = idx + 1; idx != -1;)
84                         {
85                                 idx = cts.IndexOf (";", start);
86                                 string body;
87                                 if (idx == -1)
88                                         body = cts.Substring (start);
89                                 else 
90                                 {
91                                         body = cts.Substring (start, idx - start);
92                                         start = idx + 1;
93                                 }
94                                 body = body.Trim ();
95                                 if (body.StartsWith ("charset="))
96                                 {
97                                         encoding = body.Substring (8);
98                                         encoding = encoding.TrimStart (trimChars).TrimEnd (trimChars);
99                                 }
100                         }
101
102                         return Encoding.GetEncoding (encoding);
103                 }
104
105                 public static void WriteSoapMessage (XmlTextWriter xtw, SoapMethodStubInfo method, SoapHeaderDirection dir, object bodyContent, SoapHeaderCollection headers, bool soap12)
106                 {
107                         SoapBindingUse methodUse = dir == SoapHeaderDirection.Fault ? SoapBindingUse.Literal : method.Use;
108                         XmlSerializer bodySerializer = method.GetBodySerializer (dir, soap12);
109                         XmlSerializer headerSerializer = method.GetHeaderSerializer (dir);
110                         object[] headerArray = method.GetHeaderValueArray (dir, headers);
111                         WriteSoapMessage (xtw, methodUse, bodySerializer, headerSerializer, bodyContent, headerArray, soap12);
112                 }
113                 
114                 public static void WriteSoapMessage (XmlTextWriter xtw, SoapBindingUse methodUse, XmlSerializer bodySerializer, XmlSerializer headerSerializer, object bodyContent, object[] headers, bool soap12)
115                 {
116                         string ns = soap12 ?
117                                 WebServiceHelper.Soap12EnvelopeNamespace :
118                                 WebServiceHelper.SoapEnvelopeNamespace;
119                         string encNS = soap12 ?
120                                 WebServiceHelper.Soap12EncodingNamespace :
121                                 WebServiceHelper.SoapEncodingNamespace;
122                         xtw.WriteStartDocument ();
123                         xtw.WriteStartElement ("soap", "Envelope", ns);
124                         xtw.WriteAttributeString ("xmlns", "xsi", null, XmlSchema.InstanceNamespace);
125                         xtw.WriteAttributeString ("xmlns", "xsd", null, XmlSchema.Namespace);
126
127                         // Serialize headers
128                         if (headers != null)
129                         {
130                                 xtw.WriteStartElement ("soap", "Header", ns);
131                                 headerSerializer.Serialize (xtw, headers);
132                                 xtw.WriteEndElement ();
133                         }
134
135                         // Serialize body
136                         xtw.WriteStartElement ("soap", "Body", ns);
137                         
138                         if (methodUse == SoapBindingUse.Encoded)
139                                 xtw.WriteAttributeString ("encodingStyle", ns, encNS);
140                                 
141                         bodySerializer.Serialize (xtw, bodyContent);
142
143                         xtw.WriteEndElement ();
144                         xtw.WriteEndElement ();
145                         xtw.Flush ();
146                 }
147
148                 public static void ReadSoapMessage (XmlTextReader xmlReader, SoapMethodStubInfo method, SoapHeaderDirection dir, bool soap12, out object body, out SoapHeaderCollection headers)
149                 {
150                         XmlSerializer bodySerializer = method.GetBodySerializer (dir, false);// no need to worry about soap12 arg since no call for Fault anyways here.
151                         XmlSerializer headerSerializer = method.GetHeaderSerializer (dir);
152                         ReadSoapMessage (xmlReader, bodySerializer, headerSerializer, soap12, out body, out headers);
153                 }
154                 
155                 public static void ReadSoapMessage (XmlTextReader xmlReader, XmlSerializer bodySerializer, XmlSerializer headerSerializer, bool soap12, out object body, out SoapHeaderCollection headers)
156                 {
157                         xmlReader.MoveToContent ();
158                         string ns = xmlReader.NamespaceURI;
159                         switch (ns) {
160 #if NET_2_0
161                         case WebServiceHelper.Soap12EnvelopeNamespace:
162 #endif
163                         case WebServiceHelper.SoapEnvelopeNamespace:
164                                 break;
165                         default:
166                                 throw new SoapException (String.Format ("SOAP version mismatch. Namespace '{0}' is not supported in this runtime profile.", ns), VersionMismatchFaultCode (soap12));
167                         }
168                         xmlReader.ReadStartElement ("Envelope", ns);
169
170                         headers = ReadHeaders (xmlReader, headerSerializer, ns);
171
172                         xmlReader.MoveToContent ();
173                         xmlReader.ReadStartElement ("Body", ns);
174                         xmlReader.MoveToContent ();
175                         
176                         if (xmlReader.LocalName == "Fault" && xmlReader.NamespaceURI == ns)
177                                 bodySerializer = ns == Soap12EnvelopeNamespace ? Soap12Fault.Serializer : Fault.Serializer;
178
179                         body = bodySerializer.Deserialize (xmlReader);
180                 }
181
182                 static SoapHeaderCollection ReadHeaders (XmlTextReader xmlReader, XmlSerializer headerSerializer, string ns)
183                 {
184                         SoapHeaderCollection headers = null;
185                         while (! (xmlReader.NodeType == XmlNodeType.Element && xmlReader.LocalName == "Body" && xmlReader.NamespaceURI == ns))
186                         {
187                                 if (xmlReader.NodeType == XmlNodeType.Element && xmlReader.LocalName == "Header" 
188                                     && xmlReader.NamespaceURI == ns && !xmlReader.IsEmptyElement
189                                     && headerSerializer != null)
190                                 {
191                                         xmlReader.ReadStartElement ();
192                                         xmlReader.MoveToContent ();
193                                         
194                                         HeaderSerializationHelper uh = new HeaderSerializationHelper (headerSerializer);
195                                         headers = uh.Deserialize (xmlReader);
196                                         
197                                         while (xmlReader.NodeType != XmlNodeType.EndElement)
198                                                 xmlReader.Skip ();
199                                                 
200                                         xmlReader.ReadEndElement ();
201                                 }
202                                 else
203                                         xmlReader.Skip ();
204                         }
205                         if (headers != null)
206                                 return headers;
207                         else
208                                 return new SoapHeaderCollection ();
209                 }
210                 
211                 class HeaderSerializationHelper
212                 {
213                         SoapHeaderCollection headers;
214                         XmlSerializer headerSerializer;
215                         
216                         public HeaderSerializationHelper (XmlSerializer headerSerializer)
217                         {
218                                 this.headers = new SoapHeaderCollection ();
219                                 this.headerSerializer = headerSerializer;
220                         }
221                         
222                         public SoapHeaderCollection Deserialize (XmlTextReader xmlReader)
223                         {
224                                 try {
225                                         headerSerializer.UnknownElement += new XmlElementEventHandler (OnAddUnknownHeader);
226                                         object[] headerArray = (object[]) headerSerializer.Deserialize (xmlReader);
227                                         foreach (SoapHeader h in headerArray)
228                                                 if (h != null) headers.Add (h);
229                                         return headers;
230                                 } finally {
231                                         headerSerializer.UnknownElement -= new XmlElementEventHandler (OnAddUnknownHeader);
232                                 }
233                         }
234                         
235                         void OnAddUnknownHeader (object sender, XmlElementEventArgs e)
236                         {
237                                 headers.Add (new SoapUnknownHeader (e.Element));
238                         }
239                 }
240
241 #if NET_2_0
242                 public static SoapException Soap12FaultToSoapException (Soap12Fault fault)
243                 {
244                         Soap12FaultReasonText text =
245                                 fault.Reason != null &&
246                                 fault.Reason.Texts != null &&
247                                 fault.Reason.Texts.Length > 0 ?
248                                 fault.Reason.Texts [fault.Reason.Texts.Length - 1] : null;
249                         XmlNode detail = (fault.Detail == null) ? null :
250                                 (fault.Detail.Children != null &&
251                                 fault.Detail.Children.Length > 0) ?
252                                 (XmlNode) fault.Detail.Children [0] :
253                                 (fault.Detail.Attributes != null &&
254                                 fault.Detail.Attributes.Length > 0) ?
255                                 fault.Detail.Attributes [0] : null;
256                         SoapFaultSubCode subcode = Soap12Fault.GetSoapFaultSubCode (fault.Code.Subcode);
257                         return new SoapException (
258                                 text != null ? text.Value : null,
259                                 fault.Code.Value, null, fault.Role,
260                                 text != null ? text.XmlLang : null,
261                                 detail, subcode, null);
262                 }
263 #endif
264
265                 public static XmlQualifiedName ClientFaultCode (bool soap12)
266                 {
267 #if NET_2_0
268                         return soap12 ? Soap12FaultCodes.SenderFaultCode : SoapException.ClientFaultCode;
269 #else
270                         return SoapException.ClientFaultCode;
271 #endif
272                 }
273
274                 public static XmlQualifiedName ServerFaultCode (bool soap12)
275                 {
276 #if NET_2_0
277                         return soap12 ? Soap12FaultCodes.ReceiverFaultCode : SoapException.ServerFaultCode;
278 #else
279                         return SoapException.ServerFaultCode;
280 #endif
281                 }
282
283                 public static XmlQualifiedName MustUnderstandFaultCode (bool soap12)
284                 {
285 #if NET_2_0
286                         return soap12 ? Soap12FaultCodes.ReceiverFaultCode : SoapException.MustUnderstandFaultCode;
287 #else
288                         return SoapException.MustUnderstandFaultCode;
289 #endif
290                 }
291
292                 public static XmlQualifiedName VersionMismatchFaultCode (bool soap12)
293                 {
294 #if NET_2_0
295                         return soap12 ? Soap12FaultCodes.VersionMismatchFaultCode : SoapException.VersionMismatchFaultCode;
296 #else
297                         return SoapException.VersionMismatchFaultCode;
298 #endif
299                 }
300
301                 public static void InvalidOperation (string message, WebResponse response, Encoding enc)
302                 {
303                         if (response == null)
304                                 throw new InvalidOperationException (message);
305
306                         if (enc == null)
307                                 enc = Encoding.UTF8;
308
309                         StringBuilder sb = new StringBuilder ();
310                         sb.Append (message);
311                         if (response.ContentLength > 0) {
312                                 sb.Append ("\r\nResponse error message:\r\n--\r\n");
313
314                                 try {
315                                         StreamReader resp = new StreamReader (response.GetResponseStream (), enc);
316                                         sb.Append (resp.ReadToEnd ());
317                                 } catch (Exception) {
318                                 }
319                         }
320
321                         throw new InvalidOperationException (sb.ToString ());
322                 }
323         }
324 }