* HttpGetTypeStubInfo.cs, HttpPostTypeStubInfo.cs, HttpSimpleTypeStubInfo.cs,
[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.Xml.Serialization;\r
24 using System.Xml.Schema;\r
25 using System.Collections;\r
26 using System.Threading;\r
27 \r
28 namespace System.Web.Services.Protocols {\r
29         public class SoapHttpClientProtocol : HttpWebClientProtocol {\r
30                 SoapTypeStubInfo type_info;\r
31 \r
32                 #region SoapWebClientAsyncResult class\r
33 \r
34                 internal class SoapWebClientAsyncResult: WebClientAsyncResult\r
35                 {\r
36                         public SoapWebClientAsyncResult (WebRequest request, AsyncCallback callback, object asyncState)\r
37                         : base (request, callback, asyncState)\r
38                         {\r
39                         }\r
40                 \r
41                         public SoapClientMessage Message;\r
42                         public SoapExtension[] Extensions;\r
43                 }\r
44                 #endregion\r
45 \r
46                 #region Constructors\r
47 \r
48                 public SoapHttpClientProtocol () \r
49                 {\r
50                         type_info = (SoapTypeStubInfo) TypeStubManager.GetTypeStub (this.GetType (), "Soap");\r
51                 }\r
52 \r
53                 #endregion // Constructors\r
54 \r
55                 #region Methods\r
56 \r
57                 protected IAsyncResult BeginInvoke (string methodName, object[] parameters, AsyncCallback callback, object asyncState)\r
58                 {\r
59                         SoapMethodStubInfo msi = (SoapMethodStubInfo) type_info.GetMethod (methodName);\r
60 \r
61                         SoapWebClientAsyncResult ainfo = null;\r
62                         try\r
63                         {\r
64                                 SoapClientMessage message = new SoapClientMessage (this, msi, Url, parameters);\r
65                                 message.CollectHeaders (this, message.MethodStubInfo.Headers, SoapHeaderDirection.In);\r
66                                 \r
67                                 WebRequest request = GetRequestForMessage (uri, message);\r
68                                 \r
69                                 ainfo = new SoapWebClientAsyncResult (request, callback, asyncState);\r
70                                 ainfo.Message = message;\r
71                                 ainfo.Extensions = SoapExtension.CreateExtensionChain (type_info.SoapExtensions[0], msi.SoapExtensions, type_info.SoapExtensions[1]);\r
72 \r
73                                 ainfo.Request.BeginGetRequestStream (new AsyncCallback (AsyncGetRequestStreamDone), ainfo);\r
74                         }\r
75                         catch (Exception ex)\r
76                         {\r
77                                 if (ainfo != null)\r
78                                         ainfo.SetCompleted (null, ex, false);\r
79                         }\r
80 \r
81                         return ainfo;\r
82                 }\r
83 \r
84                 void AsyncGetRequestStreamDone (IAsyncResult ar)\r
85                 {\r
86                         SoapWebClientAsyncResult ainfo = (SoapWebClientAsyncResult) ar.AsyncState;\r
87                         try\r
88                         {\r
89                                 SendRequest (ainfo.Request.EndGetRequestStream (ar), ainfo.Message, ainfo.Extensions);\r
90                                 ainfo.Request.BeginGetResponse (new AsyncCallback (AsyncGetResponseDone), ainfo);\r
91                         }\r
92                         catch (Exception ex)\r
93                         {\r
94                                 Console.WriteLine ("E1:" + ex);\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                                 Console.WriteLine ("E2:" + ex);\r
109                                 response = ex.Response;\r
110                                 HttpWebResponse http_response = response as HttpWebResponse;\r
111                                 if (http_response == null || http_response.StatusCode != HttpStatusCode.InternalServerError) {\r
112                                         ainfo.SetCompleted (null, ex, true);\r
113                                         return;\r
114                                 }\r
115                         }\r
116                         catch (Exception ex) {\r
117                                 ainfo.SetCompleted (null, ex, true);\r
118                                 return;\r
119                         }\r
120 \r
121                         try {\r
122                                 object[] result = ReceiveResponse (response, ainfo.Message, ainfo.Extensions);\r
123                                 ainfo.SetCompleted (result, null, true);\r
124                         }\r
125                         catch (Exception ex) {\r
126                                 ainfo.SetCompleted (null, ex, true);\r
127                         }\r
128                 }\r
129 \r
130                 protected object[] EndInvoke (IAsyncResult asyncResult)\r
131                 {\r
132                         if (!(asyncResult is SoapWebClientAsyncResult)) throw new ArgumentException ("asyncResult is not the return value from BeginInvoke");\r
133 \r
134                         SoapWebClientAsyncResult ainfo = (SoapWebClientAsyncResult) asyncResult;\r
135                         lock (ainfo)\r
136                         {\r
137                                 if (!ainfo.IsCompleted) ainfo.WaitForComplete ();\r
138                                 if (ainfo.Exception != null) throw ainfo.Exception;\r
139                                 else return (object[]) ainfo.Result;\r
140                         }\r
141                 }\r
142 \r
143                 [MonoTODO]\r
144                 public void Discover ()\r
145                 {\r
146                         throw new NotImplementedException ();\r
147                 }\r
148 \r
149                 protected override WebRequest GetWebRequest (Uri uri)\r
150                 {\r
151                         return base.GetWebRequest (uri);\r
152                 }\r
153 \r
154                 //\r
155                 // Just for debugging\r
156                 //\r
157                 void DumpStackFrames ()\r
158                 {\r
159                         StackTrace st = new StackTrace ();\r
160 \r
161                         for (int i = 0; i < st.FrameCount; i++){\r
162                                 StackFrame sf = st.GetFrame (i);\r
163                                 Console.WriteLine ("At frame: {0} {1}", i, sf.GetMethod ().Name);\r
164                         }\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                                 xtw.Formatting = Formatting.Indented;\r
190 \r
191                                 WebServiceHelper.WriteSoapMessage (xtw, type_info, message.MethodStubInfo.Use, message.MethodStubInfo.RequestSerializer, message.Parameters, message.Headers);\r
192 \r
193                                 if (extensions != null) {\r
194                                         message.SetStage (SoapMessageStage.AfterSerialize);\r
195                                         SoapExtension.ExecuteProcessMessage (extensions, message, true);
196                                 }
197 \r
198                                 xtw.Flush ();\r
199                                 xtw.Close ();\r
200                          }\r
201                 }\r
202 \r
203 \r
204                 //\r
205                 // TODO:\r
206                 //    Handle other web responses (multi-output?)\r
207                 //    \r
208                 object [] ReceiveResponse (WebResponse response, SoapClientMessage message, SoapExtension[] extensions)\r
209                 {\r
210                         HttpWebResponse http_response = (HttpWebResponse) response;\r
211                         HttpStatusCode code = http_response.StatusCode;\r
212                         SoapMethodStubInfo msi = message.MethodStubInfo;\r
213 \r
214                         if (!(code == HttpStatusCode.Accepted || code == HttpStatusCode.OK || code == HttpStatusCode.InternalServerError))\r
215                                 throw new Exception ("Return code was: " + http_response.StatusCode);\r
216 \r
217                         //\r
218                         // Remove optional encoding\r
219                         //\r
220                         Encoding encoding = WebServiceHelper.GetContentEncoding (response.ContentType);\r
221 \r
222                         Stream stream = response.GetResponseStream ();\r
223 \r
224                         if (extensions != null) {\r
225                                 stream = SoapExtension.ExecuteChainStream (extensions, stream);\r
226                                 message.SetStage (SoapMessageStage.BeforeDeserialize);\r
227                                 SoapExtension.ExecuteProcessMessage (extensions, message, false);
228                         }
229                         \r
230                         // Deserialize the response\r
231 \r
232                         StreamReader reader = new StreamReader (stream, encoding, false);\r
233                         XmlTextReader xml_reader = new XmlTextReader (reader);\r
234 \r
235                         bool isSuccessful = (code != HttpStatusCode.InternalServerError);\r
236                         SoapHeaderCollection headers;\r
237                         object content;\r
238 \r
239                         if (isSuccessful) {\r
240                                 WebServiceHelper.ReadSoapMessage (xml_reader, type_info, msi.Use, msi.ResponseSerializer, out content, out headers);\r
241                                 message.OutParameters = (object[]) content;\r
242                         }\r
243                         else {\r
244                                 WebServiceHelper.ReadSoapMessage (xml_reader, type_info, msi.Use, type_info.GetFaultSerializer (msi.Use), out content, out headers);\r
245                                 Fault fault = (Fault) content;\r
246                                 SoapException ex = new SoapException (fault.faultstring, fault.faultcode, fault.faultactor, fault.detail);\r
247                                 message.SetException (ex);\r
248                         }\r
249 \r
250                         message.SetHeaders (headers);\r
251                         message.UpdateHeaderValues (this, message.MethodStubInfo.Headers);\r
252 \r
253                         if (extensions != null) {\r
254                                 message.SetStage (SoapMessageStage.AfterDeserialize);\r
255                                 SoapExtension.ExecuteProcessMessage (extensions, message, false);
256                         }
257 \r
258                         if (isSuccessful)\r
259                                 return message.OutParameters;\r
260                         else\r
261                                 throw message.Exception;\r
262                 }\r
263 \r
264                 protected object[] Invoke (string method_name, object[] parameters)\r
265                 {\r
266                         SoapMethodStubInfo msi = (SoapMethodStubInfo) type_info.GetMethod (method_name);\r
267                         \r
268                         SoapClientMessage message = new SoapClientMessage (this, msi, Url, parameters);\r
269                         message.CollectHeaders (this, message.MethodStubInfo.Headers, SoapHeaderDirection.In);\r
270 \r
271                         SoapExtension[] extensions = SoapExtension.CreateExtensionChain (type_info.SoapExtensions[0], msi.SoapExtensions, type_info.SoapExtensions[1]);\r
272 \r
273                         WebResponse response;\r
274                         try\r
275                         {\r
276                                 WebRequest request = GetRequestForMessage (uri, message);\r
277                                 SendRequest (request.GetRequestStream (), message, extensions);\r
278                                 response = GetWebResponse (request);\r
279                         }\r
280                         catch (WebException ex)\r
281                         {\r
282                                 response = ex.Response;\r
283                                 HttpWebResponse http_response = response as HttpWebResponse;\r
284                                 if (http_response == null || http_response.StatusCode != HttpStatusCode.InternalServerError)\r
285                                         throw ex;\r
286                         }\r
287 \r
288                         return ReceiveResponse (response, message, extensions);\r
289                 }\r
290 \r
291                 #endregion // Methods\r
292         }\r
293 }