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