New test.
[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 {\r
52         public class SoapHttpClientProtocol : HttpWebClientProtocol \r
53         {\r
54                 SoapTypeStubInfo type_info;\r
55 #if NET_2_0\r
56                 WsiClaims conformanceClaims;\r
57                 SoapProtocolVersion soapVersion;\r
58 #endif\r
59 \r
60                 #region SoapWebClientAsyncResult class\r
61 \r
62                 internal class SoapWebClientAsyncResult: WebClientAsyncResult\r
63                 {\r
64                         public SoapWebClientAsyncResult (WebRequest request, AsyncCallback callback, object asyncState)\r
65                         : base (request, callback, asyncState)\r
66                         {\r
67                         }\r
68                 \r
69                         public SoapClientMessage Message;\r
70                         public SoapExtension[] Extensions;\r
71                 }\r
72                 #endregion\r
73 \r
74                 #region Constructors\r
75 \r
76                 public SoapHttpClientProtocol () \r
77                 {\r
78                         type_info = (SoapTypeStubInfo) TypeStubManager.GetTypeStub (this.GetType (), "Soap");\r
79                 }\r
80 \r
81                 #endregion // Constructors\r
82 \r
83                 #region Methods\r
84 \r
85                 protected IAsyncResult BeginInvoke (string methodName, object[] parameters, AsyncCallback callback, object asyncState)\r
86                 {\r
87                         SoapMethodStubInfo msi = (SoapMethodStubInfo) type_info.GetMethod (methodName);\r
88 \r
89                         SoapWebClientAsyncResult ainfo = null;\r
90                         try\r
91                         {\r
92                                 SoapClientMessage message = new SoapClientMessage (this, msi, Url, parameters);\r
93                                 message.CollectHeaders (this, message.MethodStubInfo.Headers, SoapHeaderDirection.In);\r
94                                 \r
95                                 WebRequest request = GetRequestForMessage (uri, message);\r
96                                 \r
97                                 ainfo = new SoapWebClientAsyncResult (request, callback, asyncState);\r
98                                 ainfo.Message = message;\r
99                                 ainfo.Extensions = SoapExtension.CreateExtensionChain (type_info.SoapExtensions[0], msi.SoapExtensions, type_info.SoapExtensions[1]);\r
100 \r
101                                 ainfo.Request.BeginGetRequestStream (new AsyncCallback (AsyncGetRequestStreamDone), ainfo);\r
102                         }\r
103                         catch (Exception ex)\r
104                         {\r
105                                 if (ainfo != null)\r
106                                         ainfo.SetCompleted (null, ex, false);\r
107                         }\r
108 \r
109                         return ainfo;\r
110                 }\r
111 \r
112                 void AsyncGetRequestStreamDone (IAsyncResult ar)\r
113                 {\r
114                         SoapWebClientAsyncResult ainfo = (SoapWebClientAsyncResult) ar.AsyncState;\r
115                         try\r
116                         {\r
117                                 SendRequest (ainfo.Request.EndGetRequestStream (ar), ainfo.Message, ainfo.Extensions);\r
118                                 ainfo.Request.BeginGetResponse (new AsyncCallback (AsyncGetResponseDone), ainfo);\r
119                         }\r
120                         catch (Exception ex)\r
121                         {\r
122                                 ainfo.SetCompleted (null, ex, true);\r
123                         }\r
124                 }\r
125 \r
126                 void AsyncGetResponseDone (IAsyncResult ar)\r
127                 {\r
128                         SoapWebClientAsyncResult ainfo = (SoapWebClientAsyncResult) ar.AsyncState;\r
129                         WebResponse response = null;\r
130 \r
131                         try {\r
132                                 response = GetWebResponse (ainfo.Request, ar);\r
133                         }\r
134                         catch (WebException ex) {\r
135                                 response = ex.Response;\r
136                                 HttpWebResponse http_response = response as HttpWebResponse;\r
137                                 if (http_response == null || http_response.StatusCode != HttpStatusCode.InternalServerError) {\r
138                                         ainfo.SetCompleted (null, ex, true);\r
139                                         return;\r
140                                 }\r
141                         }\r
142                         catch (Exception ex) {\r
143                                 ainfo.SetCompleted (null, ex, true);\r
144                                 return;\r
145                         }\r
146 \r
147                         try {\r
148                                 object[] result = ReceiveResponse (response, ainfo.Message, ainfo.Extensions);\r
149                                 ainfo.SetCompleted (result, null, true);\r
150                         }\r
151                         catch (Exception ex) {\r
152                                 ainfo.SetCompleted (null, ex, true);\r
153                         }\r
154                         finally {
155                                 response.Close();
156                         }
157                 }\r
158 \r
159                 protected object[] EndInvoke (IAsyncResult asyncResult)\r
160                 {\r
161                         if (!(asyncResult is SoapWebClientAsyncResult)) throw new ArgumentException ("asyncResult is not the return value from BeginInvoke");\r
162 \r
163                         SoapWebClientAsyncResult ainfo = (SoapWebClientAsyncResult) asyncResult;\r
164                         lock (ainfo)\r
165                         {\r
166                                 if (!ainfo.IsCompleted) ainfo.WaitForComplete ();\r
167                                 if (ainfo.Exception != null) throw ainfo.Exception;\r
168                                 else return (object[]) ainfo.Result;\r
169                         }\r
170                 }\r
171 \r
172                 public void Discover ()\r
173                 {\r
174                         BindingInfo bnd = (BindingInfo) type_info.Bindings [0];\r
175                         \r
176                         DiscoveryClientProtocol discoverer = new DiscoveryClientProtocol ();\r
177                         discoverer.Discover (Url);\r
178                         \r
179                         foreach (object info in discoverer.AdditionalInformation)\r
180                         {\r
181                                 System.Web.Services.Discovery.SoapBinding sb = info as System.Web.Services.Discovery.SoapBinding;\r
182                                 if (sb != null && sb.Binding.Name == bnd.Name && sb.Binding.Namespace == bnd.Namespace) {\r
183                                         Url = sb.Address;\r
184                                         return;\r
185                                 }\r
186                         }\r
187                         \r
188                         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
189                         throw new Exception (msg);\r
190                 }\r
191 \r
192                 protected override WebRequest GetWebRequest (Uri uri)\r
193                 {\r
194                         return base.GetWebRequest (uri);\r
195                 }\r
196 \r
197                 WebRequest GetRequestForMessage (Uri uri, SoapClientMessage message)\r
198                 {\r
199                         WebRequest request = GetWebRequest (uri);\r
200                         request.Method = "POST";\r
201                         WebHeaderCollection headers = request.Headers;\r
202                         headers.Add ("SOAPAction", "\"" + message.Action + "\"");\r
203                         request.ContentType = message.ContentType + "; charset=utf-8";\r
204                         return request;\r
205                 }\r
206                 \r
207                 void SendRequest (Stream s, SoapClientMessage message, SoapExtension[] extensions)\r
208                 {\r
209                         using (s) {\r
210 \r
211                                 if (extensions != null) {\r
212                                         s = SoapExtension.ExecuteChainStream (extensions, s);\r
213                                         message.SetStage (SoapMessageStage.BeforeSerialize);\r
214                                         SoapExtension.ExecuteProcessMessage (extensions, message, s, true);
215                                 }
216 \r
217                                 XmlTextWriter xtw = WebServiceHelper.CreateXmlWriter (s);\r
218                                 \r
219                                 WebServiceHelper.WriteSoapMessage (xtw, message.MethodStubInfo, SoapHeaderDirection.In, message.Parameters, message.Headers);\r
220 \r
221                                 if (extensions != null) {\r
222                                         message.SetStage (SoapMessageStage.AfterSerialize);\r
223                                         SoapExtension.ExecuteProcessMessage (extensions, message, s, true);
224                                 }
225 \r
226                                 xtw.Flush ();\r
227                                 xtw.Close ();\r
228                          }\r
229                 }\r
230 \r
231 \r
232                 //\r
233                 // TODO:\r
234                 //    Handle other web responses (multi-output?)\r
235                 //    \r
236                 object [] ReceiveResponse (WebResponse response, SoapClientMessage message, SoapExtension[] extensions)\r
237                 {\r
238                         SoapMethodStubInfo msi = message.MethodStubInfo;\r
239                         HttpWebResponse http_response = response as HttpWebResponse;\r
240                         \r
241                         if (http_response != null)\r
242                         {\r
243                                 HttpStatusCode code = http_response.StatusCode;\r
244         \r
245                                 if (!(code == HttpStatusCode.Accepted || code == HttpStatusCode.OK || code == HttpStatusCode.InternalServerError)) {\r
246                                         string msg = "The request failed with HTTP status {0}: {1}";\r
247                                         msg = String.Format (msg, (int) code, code);\r
248                                         throw new WebException (msg, null, WebExceptionStatus.ProtocolError, http_response);\r
249                                 }
250                                 if (message.OneWay && response.ContentLength <= 0 && (code == HttpStatusCode.Accepted || code == HttpStatusCode.OK)) {\r
251                                         return new object[0];\r
252                                 }\r
253                         }\r
254                         \r
255                         //\r
256                         // Remove optional encoding\r
257                         //\r
258                         string ctype;
259                         Encoding encoding = WebServiceHelper.GetContentEncoding (response.ContentType, out ctype);\r
260                         if (ctype != "text/xml")
261                                 WebServiceHelper.InvalidOperation (
262                                         "Content is not 'text/xml' but '" + response.ContentType + "'",
263                                         response, encoding);
264 \r
265                         message.ContentType = ctype;\r
266                         message.ContentEncoding = encoding.WebName;\r
267                         \r
268                         Stream stream = response.GetResponseStream ();\r
269 \r
270                         if (extensions != null) {\r
271                                 stream = SoapExtension.ExecuteChainStream (extensions, stream);\r
272                                 message.SetStage (SoapMessageStage.BeforeDeserialize);\r
273                                 SoapExtension.ExecuteProcessMessage (extensions, message, stream, false);
274                         }
275                         \r
276                         // Deserialize the response\r
277 \r
278                         SoapHeaderCollection headers;\r
279                         object content;\r
280 \r
281                         using (StreamReader reader = new StreamReader (stream, encoding, false)) {
282                                 XmlTextReader xml_reader = new XmlTextReader (reader);
283
284                                 WebServiceHelper.ReadSoapMessage (xml_reader, msi, SoapHeaderDirection.Out, out content, out headers);
285                         }
286
287                         \r
288                         if (content is Fault)\r
289                         {\r
290                                 Fault fault = (Fault) content;\r
291                                 SoapException ex = new SoapException (fault.faultstring, fault.faultcode, fault.faultactor, fault.detail);\r
292                                 message.SetException (ex);\r
293                         }\r
294                         else\r
295                                 message.OutParameters = (object[]) content;\r
296                         \r
297                         message.SetHeaders (headers);\r
298                         message.UpdateHeaderValues (this, message.MethodStubInfo.Headers);\r
299 \r
300                         if (extensions != null) {\r
301                                 message.SetStage (SoapMessageStage.AfterDeserialize);\r
302                                 SoapExtension.ExecuteProcessMessage (extensions, message, stream, false);
303                         }
304 \r
305                         if (message.Exception == null)\r
306                                 return message.OutParameters;\r
307                         else\r
308                                 throw message.Exception;\r
309                 }\r
310 \r
311                 protected object[] Invoke (string method_name, object[] parameters)\r
312                 {\r
313                         SoapMethodStubInfo msi = (SoapMethodStubInfo) type_info.GetMethod (method_name);\r
314                         \r
315                         SoapClientMessage message = new SoapClientMessage (this, msi, Url, parameters);\r
316                         message.CollectHeaders (this, message.MethodStubInfo.Headers, SoapHeaderDirection.In);\r
317 \r
318                         SoapExtension[] extensions = SoapExtension.CreateExtensionChain (type_info.SoapExtensions[0], msi.SoapExtensions, type_info.SoapExtensions[1]);\r
319 \r
320                         WebResponse response;\r
321                         try\r
322                         {\r
323                                 WebRequest request = GetRequestForMessage (uri, message);\r
324                                 SendRequest (request.GetRequestStream (), message, extensions);\r
325                                 response = GetWebResponse (request);\r
326                         }\r
327                         catch (WebException ex)\r
328                         {\r
329                                 response = ex.Response;\r
330                                 HttpWebResponse http_response = response as HttpWebResponse;\r
331                                 if (http_response == null || http_response.StatusCode != HttpStatusCode.InternalServerError)\r
332                                         throw ex;\r
333                         }\r
334 \r
335                         try {
336                                 return ReceiveResponse (response, message, extensions);\r
337                         }
338                         finally {
339                                 response.Close();
340                         }
341                 }\r
342                 \r
343 #if NET_2_0\r
344 \r
345                 [MonoTODO ("Do something with this")]\r
346                 [System.Runtime.InteropServices.ComVisible(false)]\r
347                 [Obsolete]\r
348                 public WsiClaims ConformanceClaims {\r
349                         get { return conformanceClaims; }\r
350                         set { conformanceClaims = value; }\r
351                 }\r
352                 \r
353                 [MonoTODO ("Do something with this")]\r
354                 [System.Runtime.InteropServices.ComVisible(false)]\r
355                 public SoapProtocolVersion SoapVersion {\r
356                         get { return soapVersion; }\r
357                         set { soapVersion = value; }\r
358                 }\r
359                 \r
360                 protected void InvokeAsync (string methodName, object[] parameters, SendOrPostCallback callback)\r
361                 {\r
362                         InvokeAsync (methodName, parameters, callback, null);\r
363                 }\r
364 \r
365                 protected void InvokeAsync (string methodName, object[] parameters, SendOrPostCallback callback, object userState)\r
366                 {\r
367                         InvokeAsyncInfo info = new InvokeAsyncInfo (callback, userState);\r
368                         BeginInvoke (methodName, parameters, new AsyncCallback (InvokeAsyncCallback), info);\r
369                 }\r
370                 \r
371                 void InvokeAsyncCallback (IAsyncResult ar)\r
372                 {\r
373                         InvokeAsyncInfo info = (InvokeAsyncInfo) ar.AsyncState;\r
374                         SoapWebClientAsyncResult sar = (SoapWebClientAsyncResult) ar;\r
375                         InvokeCompletedEventArgs args = new InvokeCompletedEventArgs (sar.Exception, false, info.UserState, (object[]) sar.Result);\r
376                         if (info.Context != null)\r
377                                 info.Context.Send (info.Callback, args);\r
378                         else\r
379                                 info.Callback (args);\r
380                 }\r
381 \r
382 #endif\r
383 \r
384                 #endregion // Methods\r
385         }\r
386 }
387 \r