* HtmlFormParameterReader.cs, HtmlFormParameterWriter.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 AsyncInfo class\r
33 \r
34                 internal class AsyncInfo: IAsyncResult\r
35                 {\r
36                         bool _completedSynchronously;\r
37                         bool _done;\r
38                         ManualResetEvent _waitHandle;\r
39 \r
40                         public object AsyncState \r
41                         {\r
42                                 get { return InternalAsyncState; }\r
43                         }\r
44 \r
45                         public WaitHandle AsyncWaitHandle \r
46                         {\r
47                                 get\r
48                                 {\r
49                                         lock (this) {\r
50                                                 if (_waitHandle != null) return _waitHandle;\r
51                                                 _waitHandle = new ManualResetEvent (_done);\r
52                                                 return _waitHandle;\r
53                                         }\r
54                                 }\r
55                         }\r
56 \r
57                         public bool CompletedSynchronously \r
58                         {\r
59                                 get { return _completedSynchronously; }\r
60                         }\r
61 \r
62                         public bool IsCompleted \r
63                         {\r
64                                 get { lock (this) { return _done; } }\r
65                         }\r
66 \r
67                         internal void SetCompleted (object[] result, Exception exception, bool async)\r
68                         {\r
69                                 lock (this)\r
70                                 {\r
71                                         Exception = exception;\r
72                                         Result = result;\r
73                                         _done = true;\r
74                                         _completedSynchronously = async;\r
75                                         if (_waitHandle != null) _waitHandle.Set ();\r
76                                         Monitor.PulseAll (this);\r
77                                 }\r
78                                 if (Callback != null) Callback (this);\r
79                         }\r
80 \r
81                         internal void WaitForComplete ()\r
82                         {\r
83                                 lock (this)\r
84                                 {\r
85                                         Monitor.Wait (this);\r
86                                 }\r
87                         }\r
88 \r
89                         internal object InternalAsyncState;\r
90                         internal AsyncCallback Callback;\r
91                         internal SoapClientMessage Message;\r
92                         internal SoapExtension[] Extensions;\r
93                         internal WebRequest Request;\r
94                         internal object[] Result;\r
95                         internal Exception Exception;\r
96                 }\r
97                 #endregion\r
98 \r
99                 #region Constructors\r
100 \r
101                 public SoapHttpClientProtocol () \r
102                 {\r
103                         type_info = (SoapTypeStubInfo) TypeStubManager.GetTypeStub (this.GetType (), typeof(SoapTypeStubInfo));\r
104                 }\r
105 \r
106                 #endregion // Constructors\r
107 \r
108                 #region Methods\r
109 \r
110                 protected IAsyncResult BeginInvoke (string methodName, object[] parameters, AsyncCallback callback, object asyncState)\r
111                 {\r
112                         AsyncInfo ainfo = new AsyncInfo ();\r
113 \r
114                         SoapMethodStubInfo msi = (SoapMethodStubInfo) type_info.GetMethod (methodName);\r
115                         ainfo.Message = new SoapClientMessage (this, msi, Url, parameters);\r
116                         ainfo.Message.CollectHeaders (this, ainfo.Message.MethodStubInfo.Headers, SoapHeaderDirection.In);\r
117                         ainfo.Extensions = SoapExtension.CreateExtensionChain (type_info.SoapExtensions[0], msi.SoapExtensions, type_info.SoapExtensions[1]);\r
118                         ainfo.InternalAsyncState = asyncState;\r
119                         ainfo.Callback = callback;\r
120 \r
121                         try\r
122                         {\r
123                                 ainfo.Request = GetRequestForMessage (uri, ainfo.Message);\r
124                                 ainfo.Request.BeginGetRequestStream (new AsyncCallback (AsyncGetRequestStreamDone), ainfo);\r
125                         }\r
126                         catch (Exception ex)\r
127                         {\r
128                                 ainfo.SetCompleted (null, ex, false);\r
129                         }\r
130 \r
131                         return ainfo;\r
132                 }\r
133 \r
134                 void AsyncGetRequestStreamDone (IAsyncResult ar)\r
135                 {\r
136                         AsyncInfo ainfo = (AsyncInfo) ar.AsyncState;\r
137                         try\r
138                         {\r
139                                 SendRequest (ainfo.Request.EndGetRequestStream (ar), ainfo.Message, ainfo.Extensions);\r
140 \r
141                                 ainfo.Request.BeginGetResponse (new AsyncCallback (AsyncGetResponseDone), ainfo);\r
142                         }\r
143                         catch (Exception ex)\r
144                         {\r
145                                 ainfo.SetCompleted (null, ex, true);\r
146                         }\r
147                 }\r
148 \r
149                 void AsyncGetResponseDone (IAsyncResult ar)\r
150                 {\r
151                         AsyncInfo ainfo = (AsyncInfo) ar.AsyncState;\r
152                         WebResponse response = null;\r
153 \r
154                         try {\r
155                                 response = GetWebResponse (ainfo.Request, ar);\r
156                         }\r
157                         catch (WebException ex) {\r
158                                 response = ex.Response;\r
159                                 HttpWebResponse http_response = response as HttpWebResponse;\r
160                                 if (http_response == null || http_response.StatusCode != HttpStatusCode.InternalServerError) {\r
161                                         ainfo.SetCompleted (null, ex, true);\r
162                                         return;\r
163                                 }\r
164                         }\r
165                         catch (Exception ex) {\r
166                                 ainfo.SetCompleted (null, ex, true);\r
167                                 return;\r
168                         }\r
169 \r
170                         try {\r
171                                 object[] result = ReceiveResponse (response, ainfo.Message, ainfo.Extensions);\r
172                                 ainfo.SetCompleted (result, null, true);\r
173                         }\r
174                         catch (Exception ex) {\r
175                                 ainfo.SetCompleted (null, ex, true);\r
176                         }\r
177                 }\r
178 \r
179                 protected object[] EndInvoke (IAsyncResult asyncResult)\r
180                 {\r
181                         if (!(asyncResult is AsyncInfo)) throw new ArgumentException ("asyncResult is not the return value from BeginInvoke");\r
182 \r
183                         AsyncInfo ainfo = (AsyncInfo) asyncResult;\r
184                         lock (ainfo)\r
185                         {\r
186                                 if (!ainfo.IsCompleted) ainfo.WaitForComplete ();\r
187                                 if (ainfo.Exception != null) throw ainfo.Exception;\r
188                                 else return ainfo.Result;\r
189                         }\r
190                 }\r
191 \r
192                 [MonoTODO]\r
193                 public void Discover ()\r
194                 {\r
195                         throw new NotImplementedException ();\r
196                 }\r
197 \r
198                 protected override WebRequest GetWebRequest (Uri uri)\r
199                 {\r
200                         return base.GetWebRequest (uri);\r
201                 }\r
202 \r
203                 //\r
204                 // Just for debugging\r
205                 //\r
206                 void DumpStackFrames ()\r
207                 {\r
208                         StackTrace st = new StackTrace ();\r
209 \r
210                         for (int i = 0; i < st.FrameCount; i++){\r
211                                 StackFrame sf = st.GetFrame (i);\r
212                                 Console.WriteLine ("At frame: {0} {1}", i, sf.GetMethod ().Name);\r
213                         }\r
214                 }\r
215 \r
216                 WebRequest GetRequestForMessage (Uri uri, SoapClientMessage message)\r
217                 {\r
218                         WebRequest request = GetWebRequest (uri);\r
219                         request.Method = "POST";\r
220                         WebHeaderCollection headers = request.Headers;\r
221                         headers.Add ("SOAPAction", message.Action);\r
222                         request.ContentType = message.ContentType + "; charset=utf-8";\r
223                         return request;\r
224                 }\r
225                 \r
226                 void SendRequest (Stream s, SoapClientMessage message, SoapExtension[] extensions)\r
227                 {\r
228                         using (s) {\r
229 \r
230                                 if (extensions != null) {\r
231                                         s = SoapExtension.ExecuteChainStream (extensions, s);\r
232                                         message.SetStage (SoapMessageStage.BeforeSerialize);\r
233                                         SoapExtension.ExecuteProcessMessage (extensions, message, true);
234                                 }
235 \r
236                                 // What a waste of UTF8encoders, but it has to be thread safe.\r
237                                 XmlTextWriter xtw = new XmlTextWriter (s, new UTF8Encoding (false));\r
238                                 xtw.Formatting = Formatting.Indented;\r
239 \r
240                                 WebServiceHelper.WriteSoapMessage (xtw, type_info, message.MethodStubInfo.RequestSerializer, message.Parameters, message.Headers);\r
241 \r
242                                 if (extensions != null) {\r
243                                         message.SetStage (SoapMessageStage.AfterSerialize);\r
244                                         SoapExtension.ExecuteProcessMessage (extensions, message, true);
245                                 }
246 \r
247                                 xtw.Flush ();\r
248                                 xtw.Close ();\r
249                          }\r
250                 }\r
251 \r
252 \r
253                 //\r
254                 // TODO:\r
255                 //    Handle other web responses (multi-output?)\r
256                 //    \r
257                 object [] ReceiveResponse (WebResponse response, SoapClientMessage message, SoapExtension[] extensions)\r
258                 {\r
259                         HttpWebResponse http_response = (HttpWebResponse) response;\r
260                         HttpStatusCode code = http_response.StatusCode;\r
261                         SoapMethodStubInfo msi = message.MethodStubInfo;\r
262 \r
263                         if (!(code == HttpStatusCode.Accepted || code == HttpStatusCode.OK || code == HttpStatusCode.InternalServerError))\r
264                                 throw new Exception ("Return code was: " + http_response.StatusCode);\r
265 \r
266                         //\r
267                         // Remove optional encoding\r
268                         //\r
269                         Encoding encoding = WebServiceHelper.GetContentEncoding (response.ContentType);\r
270 \r
271                         Stream stream = response.GetResponseStream ();\r
272 \r
273                         if (extensions != null) {\r
274                                 stream = SoapExtension.ExecuteChainStream (extensions, stream);\r
275                                 message.SetStage (SoapMessageStage.BeforeDeserialize);\r
276                                 SoapExtension.ExecuteProcessMessage (extensions, message, false);
277                         }
278                         \r
279                         // Deserialize the response\r
280 \r
281                         StreamReader reader = new StreamReader (stream, encoding, false);\r
282                         XmlTextReader xml_reader = new XmlTextReader (reader);\r
283 \r
284                         bool isSuccessful = (code != HttpStatusCode.InternalServerError);\r
285                         SoapHeaderCollection headers;\r
286                         object content;\r
287 \r
288                         if (isSuccessful) {\r
289                                 WebServiceHelper.ReadSoapMessage (xml_reader, type_info, msi.ResponseSerializer, out content, out headers);\r
290                                 message.OutParameters = (object[]) content;\r
291                         }\r
292                         else {\r
293                                 WebServiceHelper.ReadSoapMessage (xml_reader, type_info, type_info.FaultSerializer, out content, out headers);\r
294                                 Fault fault = (Fault) content;\r
295                                 SoapException ex = new SoapException (fault.faultstring, fault.faultcode, fault.faultactor, fault.detail);\r
296                                 message.SetException (ex);\r
297                         }\r
298 \r
299                         message.SetHeaders (headers);\r
300                         message.UpdateHeaderValues (this, message.MethodStubInfo.Headers);\r
301 \r
302                         if (extensions != null) {\r
303                                 message.SetStage (SoapMessageStage.AfterDeserialize);\r
304                                 SoapExtension.ExecuteProcessMessage (extensions, message, false);
305                         }
306 \r
307                         if (isSuccessful)\r
308                                 return message.OutParameters;\r
309                         else\r
310                                 throw message.Exception;\r
311                 }\r
312 \r
313                 protected object[] Invoke (string method_name, object[] parameters)\r
314                 {\r
315                         SoapMethodStubInfo msi = (SoapMethodStubInfo) type_info.GetMethod (method_name);\r
316                         \r
317                         SoapClientMessage message = new SoapClientMessage (this, msi, Url, parameters);\r
318                         message.CollectHeaders (this, message.MethodStubInfo.Headers, SoapHeaderDirection.In);\r
319 \r
320                         SoapExtension[] extensions = SoapExtension.CreateExtensionChain (type_info.SoapExtensions[0], msi.SoapExtensions, type_info.SoapExtensions[1]);\r
321 \r
322                         WebResponse response;\r
323                         try\r
324                         {\r
325                                 WebRequest request = GetRequestForMessage (uri, message);\r
326                                 SendRequest (request.GetRequestStream (), message, extensions);\r
327                                 response = GetWebResponse (request);\r
328                         }\r
329                         catch (WebException ex)\r
330                         {\r
331                                 response = ex.Response;\r
332                                 HttpWebResponse http_response = response as HttpWebResponse;\r
333                                 if (http_response == null || http_response.StatusCode != HttpStatusCode.InternalServerError)\r
334                                         throw ex;\r
335                         }\r
336 \r
337                         return ReceiveResponse (response, message, extensions);\r
338                 }\r
339 \r
340                 #endregion // Methods\r
341         }\r
342 }