* HttpSoapWebServiceHandler.cs: Set the properties ContentType and
[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 \r
13 using System.IO;\r
14 using System.Net;\r
15 using System.Web;\r
16 using System.Xml;\r
17 using System.Text;\r
18 using System.Reflection;\r
19 using System.Web.Services;\r
20 using System.Diagnostics;\r
21 using System.Runtime.CompilerServices;\r
22 using System.Web.Services.Description;\r
23 using System.Web.Services.Discovery;\r
24 using System.Xml.Serialization;\r
25 using System.Xml.Schema;\r
26 using System.Collections;\r
27 using System.Threading;\r
28 \r
29 namespace System.Web.Services.Protocols {\r
30         public class SoapHttpClientProtocol : HttpWebClientProtocol {\r
31                 SoapTypeStubInfo type_info;\r
32 \r
33                 #region SoapWebClientAsyncResult class\r
34 \r
35                 internal class SoapWebClientAsyncResult: WebClientAsyncResult\r
36                 {\r
37                         public SoapWebClientAsyncResult (WebRequest request, AsyncCallback callback, object asyncState)\r
38                         : base (request, callback, asyncState)\r
39                         {\r
40                         }\r
41                 \r
42                         public SoapClientMessage Message;\r
43                         public SoapExtension[] Extensions;\r
44                 }\r
45                 #endregion\r
46 \r
47                 #region Constructors\r
48 \r
49                 public SoapHttpClientProtocol () \r
50                 {\r
51                         type_info = (SoapTypeStubInfo) TypeStubManager.GetTypeStub (this.GetType (), "Soap");\r
52                 }\r
53 \r
54                 #endregion // Constructors\r
55 \r
56                 #region Methods\r
57 \r
58                 protected IAsyncResult BeginInvoke (string methodName, object[] parameters, AsyncCallback callback, object asyncState)\r
59                 {\r
60                         SoapMethodStubInfo msi = (SoapMethodStubInfo) type_info.GetMethod (methodName);\r
61 \r
62                         SoapWebClientAsyncResult ainfo = null;\r
63                         try\r
64                         {\r
65                                 SoapClientMessage message = new SoapClientMessage (this, msi, Url, parameters);\r
66                                 message.CollectHeaders (this, message.MethodStubInfo.Headers, SoapHeaderDirection.In);\r
67                                 \r
68                                 WebRequest request = GetRequestForMessage (uri, message);\r
69                                 \r
70                                 ainfo = new SoapWebClientAsyncResult (request, callback, asyncState);\r
71                                 ainfo.Message = message;\r
72                                 ainfo.Extensions = SoapExtension.CreateExtensionChain (type_info.SoapExtensions[0], msi.SoapExtensions, type_info.SoapExtensions[1]);\r
73 \r
74                                 ainfo.Request.BeginGetRequestStream (new AsyncCallback (AsyncGetRequestStreamDone), ainfo);\r
75                         }\r
76                         catch (Exception ex)\r
77                         {\r
78                                 if (ainfo != null)\r
79                                         ainfo.SetCompleted (null, ex, false);\r
80                         }\r
81 \r
82                         return ainfo;\r
83                 }\r
84 \r
85                 void AsyncGetRequestStreamDone (IAsyncResult ar)\r
86                 {\r
87                         SoapWebClientAsyncResult ainfo = (SoapWebClientAsyncResult) ar.AsyncState;\r
88                         try\r
89                         {\r
90                                 SendRequest (ainfo.Request.EndGetRequestStream (ar), ainfo.Message, ainfo.Extensions);\r
91                                 ainfo.Request.BeginGetResponse (new AsyncCallback (AsyncGetResponseDone), ainfo);\r
92                         }\r
93                         catch (Exception ex)\r
94                         {\r
95                                 ainfo.SetCompleted (null, ex, true);\r
96                         }\r
97                 }\r
98 \r
99                 void AsyncGetResponseDone (IAsyncResult ar)\r
100                 {\r
101                         SoapWebClientAsyncResult ainfo = (SoapWebClientAsyncResult) ar.AsyncState;\r
102                         WebResponse response = null;\r
103 \r
104                         try {\r
105                                 response = GetWebResponse (ainfo.Request, ar);\r
106                         }\r
107                         catch (WebException ex) {\r
108                                 response = ex.Response;\r
109                                 HttpWebResponse http_response = response as HttpWebResponse;\r
110                                 if (http_response == null || http_response.StatusCode != HttpStatusCode.InternalServerError) {\r
111                                         ainfo.SetCompleted (null, ex, true);\r
112                                         return;\r
113                                 }\r
114                         }\r
115                         catch (Exception ex) {\r
116                                 ainfo.SetCompleted (null, ex, true);\r
117                                 return;\r
118                         }\r
119 \r
120                         try {\r
121                                 object[] result = ReceiveResponse (response, ainfo.Message, ainfo.Extensions);\r
122                                 ainfo.SetCompleted (result, null, true);\r
123                         }\r
124                         catch (Exception ex) {\r
125                                 ainfo.SetCompleted (null, ex, true);\r
126                         }\r
127                 }\r
128 \r
129                 protected object[] EndInvoke (IAsyncResult asyncResult)\r
130                 {\r
131                         if (!(asyncResult is SoapWebClientAsyncResult)) throw new ArgumentException ("asyncResult is not the return value from BeginInvoke");\r
132 \r
133                         SoapWebClientAsyncResult ainfo = (SoapWebClientAsyncResult) asyncResult;\r
134                         lock (ainfo)\r
135                         {\r
136                                 if (!ainfo.IsCompleted) ainfo.WaitForComplete ();\r
137                                 if (ainfo.Exception != null) throw ainfo.Exception;\r
138                                 else return (object[]) ainfo.Result;\r
139                         }\r
140                 }\r
141 \r
142                 public void Discover ()\r
143                 {\r
144                         BindingInfo bnd = (BindingInfo) type_info.Bindings [0];\r
145                         \r
146                         DiscoveryClientProtocol discoverer = new DiscoveryClientProtocol ();\r
147                         discoverer.Discover (Url);\r
148                         \r
149                         foreach (object info in discoverer.AdditionalInformation)\r
150                         {\r
151                                 System.Web.Services.Discovery.SoapBinding sb = info as System.Web.Services.Discovery.SoapBinding;\r
152                                 if (sb != null && sb.Binding.Name == bnd.Name && sb.Binding.Namespace == bnd.Namespace) {\r
153                                         Url = sb.Address;\r
154                                         return;\r
155                                 }\r
156                         }\r
157                         \r
158                         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
159                         throw new Exception (msg);\r
160                 }\r
161 \r
162                 protected override WebRequest GetWebRequest (Uri uri)\r
163                 {\r
164                         return base.GetWebRequest (uri);\r
165                 }\r
166 \r
167                 WebRequest GetRequestForMessage (Uri uri, SoapClientMessage message)\r
168                 {\r
169                         WebRequest request = GetWebRequest (uri);\r
170                         request.Method = "POST";\r
171                         WebHeaderCollection headers = request.Headers;\r
172                         headers.Add ("SOAPAction", "\"" + message.Action + "\"");\r
173                         request.ContentType = message.ContentType + "; charset=utf-8";\r
174                         return request;\r
175                 }\r
176                 \r
177                 void SendRequest (Stream s, SoapClientMessage message, SoapExtension[] extensions)\r
178                 {\r
179                         using (s) {\r
180 \r
181                                 if (extensions != null) {\r
182                                         s = SoapExtension.ExecuteChainStream (extensions, s);\r
183                                         message.SetStage (SoapMessageStage.BeforeSerialize);\r
184                                         SoapExtension.ExecuteProcessMessage (extensions, message, true);
185                                 }
186 \r
187                                 // What a waste of UTF8encoders, but it has to be thread safe.\r
188                                 XmlTextWriter xtw = new XmlTextWriter (s, new UTF8Encoding (false));\r
189 \r
190                                 WebServiceHelper.WriteSoapMessage (xtw, type_info, message.MethodStubInfo.Use, message.MethodStubInfo.RequestSerializer, message.Parameters, message.Headers);\r
191 \r
192                                 if (extensions != null) {\r
193                                         message.SetStage (SoapMessageStage.AfterSerialize);\r
194                                         SoapExtension.ExecuteProcessMessage (extensions, message, true);
195                                 }
196 \r
197                                 xtw.Flush ();\r
198                                 xtw.Close ();\r
199                          }\r
200                 }\r
201 \r
202 \r
203                 //\r
204                 // TODO:\r
205                 //    Handle other web responses (multi-output?)\r
206                 //    \r
207                 object [] ReceiveResponse (WebResponse response, SoapClientMessage message, SoapExtension[] extensions)\r
208                 {\r
209                         SoapMethodStubInfo msi = message.MethodStubInfo;\r
210                         HttpWebResponse http_response = response as HttpWebResponse;\r
211                         \r
212                         if (http_response != null)\r
213                         {\r
214                                 HttpStatusCode code = http_response.StatusCode;\r
215         \r
216                                 if (!(code == HttpStatusCode.Accepted || code == HttpStatusCode.OK || code == HttpStatusCode.InternalServerError))\r
217                                         throw new WebException ("Request error. Return code was: " + http_response.StatusCode);\r
218                         }\r
219                         \r
220                         //\r
221                         // Remove optional encoding\r
222                         //\r
223                         string ctype;
224                         Encoding encoding = WebServiceHelper.GetContentEncoding (response.ContentType, out ctype);\r
225                         if (ctype != "text/xml")
226                                 WebServiceHelper.InvalidOperation (
227                                         "Content is not 'text/xml' but '" + response.ContentType + "'",
228                                         response, encoding);
229 \r
230                         message.ContentType = ctype;\r
231                         message.ContentEncoding = encoding.WebName;\r
232                         \r
233                         Stream stream = response.GetResponseStream ();\r
234 \r
235                         if (extensions != null) {\r
236                                 stream = SoapExtension.ExecuteChainStream (extensions, stream);\r
237                                 message.SetStage (SoapMessageStage.BeforeDeserialize);\r
238                                 SoapExtension.ExecuteProcessMessage (extensions, message, false);
239                         }
240                         \r
241                         // Deserialize the response\r
242 \r
243                         StreamReader reader = new StreamReader (stream, encoding, false);\r
244                         XmlTextReader xml_reader = new XmlTextReader (reader);\r
245 \r
246                         SoapHeaderCollection headers;\r
247                         object content;\r
248 \r
249                         WebServiceHelper.ReadSoapMessage (xml_reader, type_info, msi.Use, msi.ResponseSerializer, out content, out headers);\r
250                         \r
251                         if (content is Fault)\r
252                         {\r
253                                 Fault fault = (Fault) content;\r
254                                 SoapException ex = new SoapException (fault.faultstring, fault.faultcode, fault.faultactor, fault.detail);\r
255                                 message.SetException (ex);\r
256                         }\r
257                         else\r
258                                 message.OutParameters = (object[]) content;\r
259                         \r
260                         message.SetHeaders (headers);\r
261                         message.UpdateHeaderValues (this, message.MethodStubInfo.Headers);\r
262 \r
263                         if (extensions != null) {\r
264                                 message.SetStage (SoapMessageStage.AfterDeserialize);\r
265                                 SoapExtension.ExecuteProcessMessage (extensions, message, false);
266                         }
267 \r
268                         if (message.Exception == null)\r
269                                 return message.OutParameters;\r
270                         else\r
271                                 throw message.Exception;\r
272                 }\r
273 \r
274                 protected object[] Invoke (string method_name, object[] parameters)\r
275                 {\r
276                         SoapMethodStubInfo msi = (SoapMethodStubInfo) type_info.GetMethod (method_name);\r
277                         \r
278                         SoapClientMessage message = new SoapClientMessage (this, msi, Url, parameters);\r
279                         message.CollectHeaders (this, message.MethodStubInfo.Headers, SoapHeaderDirection.In);\r
280 \r
281                         SoapExtension[] extensions = SoapExtension.CreateExtensionChain (type_info.SoapExtensions[0], msi.SoapExtensions, type_info.SoapExtensions[1]);\r
282 \r
283                         WebResponse response;\r
284                         try\r
285                         {\r
286                                 WebRequest request = GetRequestForMessage (uri, message);\r
287                                 SendRequest (request.GetRequestStream (), message, extensions);\r
288                                 response = GetWebResponse (request);\r
289                         }\r
290                         catch (WebException ex)\r
291                         {\r
292                                 response = ex.Response;\r
293                                 HttpWebResponse http_response = response as HttpWebResponse;\r
294                                 if (http_response == null || http_response.StatusCode != HttpStatusCode.InternalServerError)\r
295                                         throw ex;\r
296                         }\r
297 \r
298                         return ReceiveResponse (response, message, extensions);\r
299                 }\r
300 \r
301                 #endregion // Methods\r
302         }\r
303 }