This commit was manufactured by cvs2svn to create branch 'mono-1-0'.
[mono.git] / mcs / class / System.Web.Services / System.Web.Services.Protocols / SoapHttpClientProtocol.cs
1 // \r
2 // System.Web.Services.Protocols.SoapHttpClientProtocol.cs\r
3 //\r
4 // Author:\r
5 //   Tim Coleman (tim@timcoleman.com)\r
6 //   Miguel de Icaza (miguel@ximian.com)\r
7 //   Lluis Sanchez Gual (lluis@ximian.com)\r
8 //\r
9 // Copyright (C) Tim Coleman, 2002\r
10 // Copyright (C) Ximian, Inc, 2003\r
11 //\r
12
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33 \r
34 using System.IO;\r
35 using System.Net;\r
36 using System.Web;\r
37 using System.Xml;\r
38 using System.Text;\r
39 using System.Reflection;\r
40 using System.Web.Services;\r
41 using System.Diagnostics;\r
42 using System.Runtime.CompilerServices;\r
43 using System.Web.Services.Description;\r
44 using System.Web.Services.Discovery;\r
45 using System.Xml.Serialization;\r
46 using System.Xml.Schema;\r
47 using System.Collections;\r
48 using System.Threading;\r
49 \r
50 namespace System.Web.Services.Protocols {\r
51         public class SoapHttpClientProtocol : HttpWebClientProtocol {\r
52                 SoapTypeStubInfo type_info;\r
53 \r
54                 #region SoapWebClientAsyncResult class\r
55 \r
56                 internal class SoapWebClientAsyncResult: WebClientAsyncResult\r
57                 {\r
58                         public SoapWebClientAsyncResult (WebRequest request, AsyncCallback callback, object asyncState)\r
59                         : base (request, callback, asyncState)\r
60                         {\r
61                         }\r
62                 \r
63                         public SoapClientMessage Message;\r
64                         public SoapExtension[] Extensions;\r
65                 }\r
66                 #endregion\r
67 \r
68                 #region Constructors\r
69 \r
70                 public SoapHttpClientProtocol () \r
71                 {\r
72                         type_info = (SoapTypeStubInfo) TypeStubManager.GetTypeStub (this.GetType (), "Soap");\r
73                 }\r
74 \r
75                 #endregion // Constructors\r
76 \r
77                 #region Methods\r
78 \r
79                 protected IAsyncResult BeginInvoke (string methodName, object[] parameters, AsyncCallback callback, object asyncState)\r
80                 {\r
81                         SoapMethodStubInfo msi = (SoapMethodStubInfo) type_info.GetMethod (methodName);\r
82 \r
83                         SoapWebClientAsyncResult ainfo = null;\r
84                         try\r
85                         {\r
86                                 SoapClientMessage message = new SoapClientMessage (this, msi, Url, parameters);\r
87                                 message.CollectHeaders (this, message.MethodStubInfo.Headers, SoapHeaderDirection.In);\r
88                                 \r
89                                 WebRequest request = GetRequestForMessage (uri, message);\r
90                                 \r
91                                 ainfo = new SoapWebClientAsyncResult (request, callback, asyncState);\r
92                                 ainfo.Message = message;\r
93                                 ainfo.Extensions = SoapExtension.CreateExtensionChain (type_info.SoapExtensions[0], msi.SoapExtensions, type_info.SoapExtensions[1]);\r
94 \r
95                                 ainfo.Request.BeginGetRequestStream (new AsyncCallback (AsyncGetRequestStreamDone), ainfo);\r
96                         }\r
97                         catch (Exception ex)\r
98                         {\r
99                                 if (ainfo != null)\r
100                                         ainfo.SetCompleted (null, ex, false);\r
101                         }\r
102 \r
103                         return ainfo;\r
104                 }\r
105 \r
106                 void AsyncGetRequestStreamDone (IAsyncResult ar)\r
107                 {\r
108                         SoapWebClientAsyncResult ainfo = (SoapWebClientAsyncResult) ar.AsyncState;\r
109                         try\r
110                         {\r
111                                 SendRequest (ainfo.Request.EndGetRequestStream (ar), ainfo.Message, ainfo.Extensions);\r
112                                 ainfo.Request.BeginGetResponse (new AsyncCallback (AsyncGetResponseDone), ainfo);\r
113                         }\r
114                         catch (Exception ex)\r
115                         {\r
116                                 ainfo.SetCompleted (null, ex, true);\r
117                         }\r
118                 }\r
119 \r
120                 void AsyncGetResponseDone (IAsyncResult ar)\r
121                 {\r
122                         SoapWebClientAsyncResult ainfo = (SoapWebClientAsyncResult) ar.AsyncState;\r
123                         WebResponse response = null;\r
124 \r
125                         try {\r
126                                 response = GetWebResponse (ainfo.Request, ar);\r
127                         }\r
128                         catch (WebException ex) {\r
129                                 response = ex.Response;\r
130                                 HttpWebResponse http_response = response as HttpWebResponse;\r
131                                 if (http_response == null || http_response.StatusCode != HttpStatusCode.InternalServerError) {\r
132                                         ainfo.SetCompleted (null, ex, true);\r
133                                         return;\r
134                                 }\r
135                         }\r
136                         catch (Exception ex) {\r
137                                 ainfo.SetCompleted (null, ex, true);\r
138                                 return;\r
139                         }\r
140 \r
141                         try {\r
142                                 object[] result = ReceiveResponse (response, ainfo.Message, ainfo.Extensions);\r
143                                 ainfo.SetCompleted (result, null, true);\r
144                         }\r
145                         catch (Exception ex) {\r
146                                 ainfo.SetCompleted (null, ex, true);\r
147                         }\r
148                 }\r
149 \r
150                 protected object[] EndInvoke (IAsyncResult asyncResult)\r
151                 {\r
152                         if (!(asyncResult is SoapWebClientAsyncResult)) throw new ArgumentException ("asyncResult is not the return value from BeginInvoke");\r
153 \r
154                         SoapWebClientAsyncResult ainfo = (SoapWebClientAsyncResult) asyncResult;\r
155                         lock (ainfo)\r
156                         {\r
157                                 if (!ainfo.IsCompleted) ainfo.WaitForComplete ();\r
158                                 if (ainfo.Exception != null) throw ainfo.Exception;\r
159                                 else return (object[]) ainfo.Result;\r
160                         }\r
161                 }\r
162 \r
163                 public void Discover ()\r
164                 {\r
165                         BindingInfo bnd = (BindingInfo) type_info.Bindings [0];\r
166                         \r
167                         DiscoveryClientProtocol discoverer = new DiscoveryClientProtocol ();\r
168                         discoverer.Discover (Url);\r
169                         \r
170                         foreach (object info in discoverer.AdditionalInformation)\r
171                         {\r
172                                 System.Web.Services.Discovery.SoapBinding sb = info as System.Web.Services.Discovery.SoapBinding;\r
173                                 if (sb != null && sb.Binding.Name == bnd.Name && sb.Binding.Namespace == bnd.Namespace) {\r
174                                         Url = sb.Address;\r
175                                         return;\r
176                                 }\r
177                         }\r
178                         \r
179                         string msg = string.Format ("The binding named '{0}' from namespace '{1}' was not found in the discovery document at '{2}'", bnd.Name, bnd.Namespace, Url);\r
180                         throw new Exception (msg);\r
181                 }\r
182 \r
183                 protected override WebRequest GetWebRequest (Uri uri)\r
184                 {\r
185                         return base.GetWebRequest (uri);\r
186                 }\r
187 \r
188                 WebRequest GetRequestForMessage (Uri uri, SoapClientMessage message)\r
189                 {\r
190                         WebRequest request = GetWebRequest (uri);\r
191                         request.Method = "POST";\r
192                         WebHeaderCollection headers = request.Headers;\r
193                         headers.Add ("SOAPAction", "\"" + message.Action + "\"");\r
194                         request.ContentType = message.ContentType + "; charset=utf-8";\r
195                         return request;\r
196                 }\r
197                 \r
198                 void SendRequest (Stream s, SoapClientMessage message, SoapExtension[] extensions)\r
199                 {\r
200                         using (s) {\r
201 \r
202                                 if (extensions != null) {\r
203                                         s = SoapExtension.ExecuteChainStream (extensions, s);\r
204                                         message.SetStage (SoapMessageStage.BeforeSerialize);\r
205                                         SoapExtension.ExecuteProcessMessage (extensions, message, true);
206                                 }
207 \r
208                                 XmlTextWriter xtw = WebServiceHelper.CreateXmlWriter (s);\r
209                                 \r
210                                 WebServiceHelper.WriteSoapMessage (xtw, type_info, message.MethodStubInfo.Use, message.MethodStubInfo.RequestSerializer, message.Parameters, message.Headers);\r
211 \r
212                                 if (extensions != null) {\r
213                                         message.SetStage (SoapMessageStage.AfterSerialize);\r
214                                         SoapExtension.ExecuteProcessMessage (extensions, message, true);
215                                 }
216 \r
217                                 xtw.Flush ();\r
218                                 xtw.Close ();\r
219                          }\r
220                 }\r
221 \r
222 \r
223                 //\r
224                 // TODO:\r
225                 //    Handle other web responses (multi-output?)\r
226                 //    \r
227                 object [] ReceiveResponse (WebResponse response, SoapClientMessage message, SoapExtension[] extensions)\r
228                 {\r
229                         SoapMethodStubInfo msi = message.MethodStubInfo;\r
230                         HttpWebResponse http_response = response as HttpWebResponse;\r
231                         \r
232                         if (http_response != null)\r
233                         {\r
234                                 HttpStatusCode code = http_response.StatusCode;\r
235         \r
236                                 if (!(code == HttpStatusCode.Accepted || code == HttpStatusCode.OK || code == HttpStatusCode.InternalServerError)) {\r
237                                         string msg = "The request failed with HTTP status {0}: {1}";\r
238                                         msg = String.Format (msg, (int) code, code);\r
239                                         throw new WebException (msg, null, WebExceptionStatus.ProtocolError, http_response);\r
240                                 }\r
241                         }\r
242                         \r
243                         //\r
244                         // Remove optional encoding\r
245                         //\r
246                         string ctype;
247                         Encoding encoding = WebServiceHelper.GetContentEncoding (response.ContentType, out ctype);\r
248                         if (ctype != "text/xml")
249                                 WebServiceHelper.InvalidOperation (
250                                         "Content is not 'text/xml' but '" + response.ContentType + "'",
251                                         response, encoding);
252 \r
253                         message.ContentType = ctype;\r
254                         message.ContentEncoding = encoding.WebName;\r
255                         \r
256                         Stream stream = response.GetResponseStream ();\r
257 \r
258                         if (extensions != null) {\r
259                                 stream = SoapExtension.ExecuteChainStream (extensions, stream);\r
260                                 message.SetStage (SoapMessageStage.BeforeDeserialize);\r
261                                 SoapExtension.ExecuteProcessMessage (extensions, message, false);
262                         }
263                         \r
264                         // Deserialize the response\r
265 \r
266                         StreamReader reader = new StreamReader (stream, encoding, false);\r
267                         XmlTextReader xml_reader = new XmlTextReader (reader);\r
268 \r
269                         SoapHeaderCollection headers;\r
270                         object content;\r
271 \r
272                         WebServiceHelper.ReadSoapMessage (xml_reader, type_info, msi.Use, msi.ResponseSerializer, out content, out headers);\r
273                         \r
274                         if (content is Fault)\r
275                         {\r
276                                 Fault fault = (Fault) content;\r
277                                 SoapException ex = new SoapException (fault.faultstring, fault.faultcode, fault.faultactor, fault.detail);\r
278                                 message.SetException (ex);\r
279                         }\r
280                         else\r
281                                 message.OutParameters = (object[]) content;\r
282                         \r
283                         message.SetHeaders (headers);\r
284                         message.UpdateHeaderValues (this, message.MethodStubInfo.Headers);\r
285 \r
286                         if (extensions != null) {\r
287                                 message.SetStage (SoapMessageStage.AfterDeserialize);\r
288                                 SoapExtension.ExecuteProcessMessage (extensions, message, false);
289                         }
290 \r
291                         if (message.Exception == null)\r
292                                 return message.OutParameters;\r
293                         else\r
294                                 throw message.Exception;\r
295                 }\r
296 \r
297                 protected object[] Invoke (string method_name, object[] parameters)\r
298                 {\r
299                         SoapMethodStubInfo msi = (SoapMethodStubInfo) type_info.GetMethod (method_name);\r
300                         \r
301                         SoapClientMessage message = new SoapClientMessage (this, msi, Url, parameters);\r
302                         message.CollectHeaders (this, message.MethodStubInfo.Headers, SoapHeaderDirection.In);\r
303 \r
304                         SoapExtension[] extensions = SoapExtension.CreateExtensionChain (type_info.SoapExtensions[0], msi.SoapExtensions, type_info.SoapExtensions[1]);\r
305 \r
306                         WebResponse response;\r
307                         try\r
308                         {\r
309                                 WebRequest request = GetRequestForMessage (uri, message);\r
310                                 SendRequest (request.GetRequestStream (), message, extensions);\r
311                                 response = GetWebResponse (request);\r
312                         }\r
313                         catch (WebException ex)\r
314                         {\r
315                                 response = ex.Response;\r
316                                 HttpWebResponse http_response = response as HttpWebResponse;\r
317                                 if (http_response == null || http_response.StatusCode != HttpStatusCode.InternalServerError)\r
318                                         throw ex;\r
319                         }\r
320 \r
321                         return ReceiveResponse (response, message, extensions);\r
322                 }\r
323 \r
324                 #endregion // Methods\r
325         }\r
326 }