2 // System.Web.Services.Protocols.SoapHttpClientProtocol.cs
5 // Tim Coleman (tim@timcoleman.com)
6 // Miguel de Icaza (miguel@ximian.com)
7 // Lluis Sanchez Gual (lluis@ximian.com)
9 // Copyright (C) Tim Coleman, 2002
10 // Copyright (C) Ximian, Inc, 2003
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:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
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.
34 using System
.ComponentModel
;
35 using System
.Globalization
;
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
;
52 namespace System
.Web
.Services
.Protocols
55 [System
.Runtime
.InteropServices
.ComVisible (true)]
57 public class SoapHttpClientProtocol
: HttpWebClientProtocol
59 SoapTypeStubInfo type_info
;
61 SoapProtocolVersion soapVersion
;
64 #region SoapWebClientAsyncResult class
66 internal class SoapWebClientAsyncResult
: WebClientAsyncResult
68 public SoapWebClientAsyncResult (WebRequest request
, AsyncCallback callback
, object asyncState
)
69 : base (request
, callback
, asyncState
)
73 public SoapClientMessage Message
;
74 public SoapExtension
[] Extensions
;
80 public SoapHttpClientProtocol ()
82 type_info
= (SoapTypeStubInfo
) TypeStubManager
.GetTypeStub (this.GetType (), "Soap");
85 #endregion // Constructors
89 protected IAsyncResult
BeginInvoke (string methodName
, object[] parameters
, AsyncCallback callback
, object asyncState
)
91 SoapMethodStubInfo msi
= (SoapMethodStubInfo
) type_info
.GetMethod (methodName
);
93 SoapWebClientAsyncResult ainfo
= null;
96 SoapClientMessage message
= new SoapClientMessage (this, msi
, Url
, parameters
);
97 message
.CollectHeaders (this, message
.MethodStubInfo
.Headers
, SoapHeaderDirection
.In
);
99 WebRequest request
= GetRequestForMessage (uri
, message
);
101 ainfo
= new SoapWebClientAsyncResult (request
, callback
, asyncState
);
102 ainfo
.Message
= message
;
103 ainfo
.Extensions
= SoapExtension
.CreateExtensionChain (type_info
.SoapExtensions
[0], msi
.SoapExtensions
, type_info
.SoapExtensions
[1]);
105 ainfo
.Request
.BeginGetRequestStream (new AsyncCallback (AsyncGetRequestStreamDone
), ainfo
);
106 RegisterMapping (asyncState
, ainfo
);
111 ainfo
.SetCompleted (null, ex
, false);
117 void AsyncGetRequestStreamDone (IAsyncResult ar
)
119 SoapWebClientAsyncResult ainfo
= (SoapWebClientAsyncResult
) ar
.AsyncState
;
122 SendRequest (ainfo
.Request
.EndGetRequestStream (ar
), ainfo
.Message
, ainfo
.Extensions
);
123 ainfo
.Request
.BeginGetResponse (new AsyncCallback (AsyncGetResponseDone
), ainfo
);
127 ainfo
.SetCompleted (null, ex
, true);
131 void AsyncGetResponseDone (IAsyncResult ar
)
133 SoapWebClientAsyncResult ainfo
= (SoapWebClientAsyncResult
) ar
.AsyncState
;
134 WebResponse response
= null;
137 response
= GetWebResponse (ainfo
.Request
, ar
);
139 catch (WebException ex
) {
140 response
= ex
.Response
;
141 HttpWebResponse http_response
= response
as HttpWebResponse
;
142 if (http_response
== null || http_response
.StatusCode
!= HttpStatusCode
.InternalServerError
) {
143 ainfo
.SetCompleted (null, ex
, true);
147 catch (Exception ex
) {
148 ainfo
.SetCompleted (null, ex
, true);
153 object[] result
= ReceiveResponse (response
, ainfo
.Message
, ainfo
.Extensions
);
154 ainfo
.SetCompleted (result
, null, true);
156 catch (Exception ex
) {
157 ainfo
.SetCompleted (null, ex
, true);
164 protected object[] EndInvoke (IAsyncResult asyncResult
)
166 if (!(asyncResult
is SoapWebClientAsyncResult
)) throw new ArgumentException ("asyncResult is not the return value from BeginInvoke");
168 SoapWebClientAsyncResult ainfo
= (SoapWebClientAsyncResult
) asyncResult
;
171 if (!ainfo
.IsCompleted
)
172 ainfo
.WaitForComplete ();
174 UnregisterMapping (ainfo
.AsyncState
);
176 if (ainfo
.Exception
!= null)
177 throw ainfo
.Exception
;
179 return (object[]) ainfo
.Result
;
183 public void Discover ()
185 BindingInfo bnd
= (BindingInfo
) type_info
.Bindings
[0];
187 DiscoveryClientProtocol discoverer
= new DiscoveryClientProtocol ();
188 discoverer
.Discover (Url
);
190 foreach (object info
in discoverer
.AdditionalInformation
)
192 System
.Web
.Services
.Discovery
.SoapBinding sb
= info
as System
.Web
.Services
.Discovery
.SoapBinding
;
193 if (sb
!= null && sb
.Binding
.Name
== bnd
.Name
&& sb
.Binding
.Namespace
== bnd
.Namespace
) {
199 string msg
= string.Format (
200 "The binding named '{0}' from namespace '{1}' was not found in the discovery document at '{2}'",
201 bnd
.Name
, bnd
.Namespace
, Url
);
202 throw new Exception (msg
);
205 protected override WebRequest
GetWebRequest (Uri uri
)
207 return base.GetWebRequest (uri
);
210 WebRequest
GetRequestForMessage (Uri uri
, SoapClientMessage message
)
212 WebRequest request
= GetWebRequest (uri
);
213 request
.Method
= "POST";
214 WebHeaderCollection headers
= request
.Headers
;
215 if (!message
.IsSoap12
)
216 headers
.Add ("SOAPAction", "\"" + message
.Action
+ "\"");
217 request
.ContentType
= message
.ContentType
+ "; charset=utf-8";
225 XmlReader
GetReaderForMessage (
226 SoapClientMessage message
, int bufferSize
)
228 throw new NotImplementedException ();
235 XmlWriter
GetWriterForMessage (
236 SoapClientMessage message
, int bufferSize
)
238 throw new NotImplementedException ();
241 void SendRequest (Stream s
, SoapClientMessage message
, SoapExtension
[] extensions
)
245 if (extensions
!= null) {
246 s
= SoapExtension
.ExecuteChainStream (extensions
, s
);
247 message
.SetStage (SoapMessageStage
.BeforeSerialize
);
248 SoapExtension
.ExecuteProcessMessage (extensions
, message
, s
, true);
251 XmlTextWriter xtw
= WebServiceHelper
.CreateXmlWriter (s
);
252 WebServiceHelper
.WriteSoapMessage (xtw
, message
.MethodStubInfo
, SoapHeaderDirection
.In
, message
.Parameters
, message
.Headers
, message
.IsSoap12
);
254 if (extensions
!= null) {
255 message
.SetStage (SoapMessageStage
.AfterSerialize
);
256 SoapExtension
.ExecuteProcessMessage (extensions
, message
, s
, true);
267 // Handle other web responses (multi-output?)
269 object [] ReceiveResponse (WebResponse response
, SoapClientMessage message
, SoapExtension
[] extensions
)
271 SoapMethodStubInfo msi
= message
.MethodStubInfo
;
272 HttpWebResponse http_response
= response
as HttpWebResponse
;
274 if (http_response
!= null)
276 HttpStatusCode code
= http_response
.StatusCode
;
278 if (!(code
== HttpStatusCode
.Accepted
|| code
== HttpStatusCode
.OK
|| code
== HttpStatusCode
.InternalServerError
)) {
279 string msg
= "The request failed with HTTP status {0}: {1}";
280 msg
= String
.Format (msg
, (int) code
, code
);
281 throw new WebException (msg
, null, WebExceptionStatus
.ProtocolError
, http_response
);
283 if (message
.OneWay
&& response
.ContentLength
<= 0 && (code
== HttpStatusCode
.Accepted
|| code
== HttpStatusCode
.OK
)) {
284 return new object[0];
289 // Remove optional encoding
292 Encoding encoding
= WebServiceHelper
.GetContentEncoding (response
.ContentType
, out ctype
);
293 ctype
= ctype
.ToLower (CultureInfo
.InvariantCulture
);
295 if ((!message
.IsSoap12
|| ctype
!= "application/soap+xml") && ctype
!= "text/xml")
297 if (ctype
!= "text/xml")
299 WebServiceHelper
.InvalidOperation (
300 String
.Format ("Not supported Content-Type in the response: '{0}'", response
.ContentType
),
303 message
.ContentType
= ctype
;
304 message
.ContentEncoding
= encoding
.WebName
;
306 Stream stream
= response
.GetResponseStream ();
308 if (extensions
!= null) {
309 stream
= SoapExtension
.ExecuteChainStream (extensions
, stream
);
310 message
.SetStage (SoapMessageStage
.BeforeDeserialize
);
311 SoapExtension
.ExecuteProcessMessage (extensions
, message
, stream
, false);
314 // Deserialize the response
316 SoapHeaderCollection headers
;
319 using (StreamReader reader
= new StreamReader (stream
, encoding
, false)) {
320 XmlTextReader xml_reader
= new XmlTextReader (reader
);
322 WebServiceHelper
.ReadSoapMessage (xml_reader
, msi
, SoapHeaderDirection
.Out
, message
.IsSoap12
, out content
, out headers
);
326 if (content
is Soap12Fault
) {
327 SoapException ex
= WebServiceHelper
.Soap12FaultToSoapException ((Soap12Fault
) content
);
328 message
.SetException (ex
);
332 if (content
is Fault
) {
333 Fault fault
= (Fault
) content
;
334 SoapException ex
= new SoapException (fault
.faultstring
, fault
.faultcode
, fault
.faultactor
, fault
.detail
);
335 message
.SetException (ex
);
338 message
.OutParameters
= (object[]) content
;
340 message
.SetHeaders (headers
);
341 message
.UpdateHeaderValues (this, message
.MethodStubInfo
.Headers
);
343 if (extensions
!= null) {
344 message
.SetStage (SoapMessageStage
.AfterDeserialize
);
345 SoapExtension
.ExecuteProcessMessage (extensions
, message
, stream
, false);
348 if (message
.Exception
== null)
349 return message
.OutParameters
;
351 throw message
.Exception
;
354 protected object[] Invoke (string method_name
, object[] parameters
)
356 SoapMethodStubInfo msi
= (SoapMethodStubInfo
) type_info
.GetMethod (method_name
);
358 SoapClientMessage message
= new SoapClientMessage (this, msi
, Url
, parameters
);
359 message
.CollectHeaders (this, message
.MethodStubInfo
.Headers
, SoapHeaderDirection
.In
);
361 SoapExtension
[] extensions
= SoapExtension
.CreateExtensionChain (type_info
.SoapExtensions
[0], msi
.SoapExtensions
, type_info
.SoapExtensions
[1]);
363 WebResponse response
;
366 WebRequest request
= GetRequestForMessage (uri
, message
);
367 SendRequest (request
.GetRequestStream (), message
, extensions
);
368 response
= GetWebResponse (request
);
370 catch (WebException ex
)
372 response
= ex
.Response
;
373 HttpWebResponse http_response
= response
as HttpWebResponse
;
374 if (http_response
== null || http_response
.StatusCode
!= HttpStatusCode
.InternalServerError
)
379 return ReceiveResponse (response
, message
, extensions
);
388 [MonoTODO ("Do something with this")]
389 [System
.Runtime
.InteropServices
.ComVisible(false)]
390 [DefaultValue (SoapProtocolVersion
.Default
)]
391 public SoapProtocolVersion SoapVersion
{
392 get { return soapVersion; }
393 set { soapVersion = value; }
396 protected void InvokeAsync (string methodName
, object[] parameters
, SendOrPostCallback callback
)
398 InvokeAsync (methodName
, parameters
, callback
, null);
401 protected void InvokeAsync (string methodName
, object[] parameters
, SendOrPostCallback callback
, object userState
)
403 InvokeAsyncInfo info
= new InvokeAsyncInfo (callback
, userState
);
404 BeginInvoke (methodName
, parameters
, new AsyncCallback (InvokeAsyncCallback
), info
);
407 void InvokeAsyncCallback (IAsyncResult ar
)
409 InvokeAsyncInfo info
= (InvokeAsyncInfo
) ar
.AsyncState
;
410 SoapWebClientAsyncResult sar
= (SoapWebClientAsyncResult
) ar
;
411 InvokeCompletedEventArgs args
= new InvokeCompletedEventArgs (sar
.Exception
, false, info
.UserState
, (object[]) sar
.Result
);
412 if (info
.Context
!= null)
413 info
.Context
.Send (info
.Callback
, args
);
415 info
.Callback (args
);
420 #endregion // Methods