* Methods.cs: Added serializer to MethodStubInfo for deserializing faults.
[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 //\r
8 // Copyright (C) Tim Coleman, 2002\r
9 // Copyright (C) Ximian, Inc, 2003\r
10 //\r
11 \r
12 using System.IO;\r
13 using System.Net;\r
14 using System.Web;\r
15 using System.Xml;\r
16 using System.Text;\r
17 using System.Reflection;\r
18 using System.Web.Services;\r
19 using System.Diagnostics;\r
20 using System.Runtime.CompilerServices;\r
21 using System.Web.Services.Description;\r
22 using System.Xml.Serialization;\r
23 namespace System.Web.Services.Protocols {\r
24         public class SoapHttpClientProtocol : HttpWebClientProtocol {\r
25                 TypeStubInfo type_info;\r
26                 \r
27                 #region Constructors\r
28 \r
29                 public SoapHttpClientProtocol () \r
30                 {\r
31                         type_info = TypeStubManager.GetTypeStub (this.GetType ());\r
32                 }\r
33 \r
34                 #endregion // Constructors\r
35 \r
36                 #region Methods\r
37 \r
38                 [MonoTODO]\r
39                 protected IAsyncResult BeginInvoke (string methodName, object[] parameters, AsyncCallback callback, object asyncState)\r
40                 {\r
41                         throw new NotImplementedException ();\r
42                 }\r
43 \r
44                 [MonoTODO]\r
45                 public void Discover ()\r
46                 {\r
47                         throw new NotImplementedException ();\r
48                 }\r
49 \r
50                 [MonoTODO]\r
51                 protected object[] EndInvoke (IAsyncResult asyncResult)\r
52                 {\r
53                         throw new NotImplementedException ();\r
54                 }\r
55 \r
56                 protected override WebRequest GetWebRequest (Uri uri)\r
57                 {\r
58                         WebRequest request = WebRequest.Create (uri);\r
59                         request.Method = "POST";\r
60 \r
61                         return request;\r
62                 }\r
63 \r
64                 //\r
65                 // Just for debugging\r
66                 //\r
67                 void DumpStackFrames ()\r
68                 {\r
69                         StackTrace st = new StackTrace ();\r
70 \r
71                         for (int i = 0; i < st.FrameCount; i++){\r
72                                 StackFrame sf = st.GetFrame (i);\r
73                                 Console.WriteLine ("At frame: {0} {1}", i, sf.GetMethod ().Name);\r
74                         }\r
75                 }\r
76                 \r
77                 const string soap_envelope = "http://schemas.xmlsoap.org/soap/envelope/";\r
78                 \r
79                 void WriteSoapEnvelope (XmlTextWriter xtw)\r
80                 {\r
81                         xtw.WriteStartDocument ();\r
82                         \r
83                         xtw.WriteStartElement ("soap", "Envelope", soap_envelope);\r
84                         xtw.WriteAttributeString ("xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance");\r
85                         xtw.WriteAttributeString ("xmlns", "xsd", null, "http://www.w3.org/2001/XMLSchema");\r
86                 }\r
87                 \r
88                 void SendRequest (WebRequest request, SoapClientMessage message)\r
89                 {\r
90                         WebHeaderCollection headers = request.Headers;\r
91                         headers.Add ("SOAPAction", message.Action);\r
92 \r
93                         request.ContentType = message.ContentType + "; charset=utf-8";\r
94 \r
95                         using (Stream s = request.GetRequestStream ()){\r
96                                 // serialize arguments\r
97 \r
98                                 // What a waste of UTF8encoders, but it has to be thread safe.\r
99                                 \r
100                                 XmlTextWriter xtw = new XmlTextWriter (s, new UTF8Encoding (false));\r
101                                 xtw.Formatting = Formatting.Indented;\r
102                                 WriteSoapEnvelope (xtw);\r
103                                 xtw.WriteStartElement ("soap", "Body", soap_envelope);\r
104 \r
105                                 // Serialize arguments.\r
106                                 message.MethodStubInfo.RequestSerializer.Serialize (xtw, message.Parameters);\r
107                                 xtw.WriteEndElement ();\r
108                                 xtw.WriteEndElement ();\r
109                                 \r
110                                 xtw.Flush ();\r
111                                 xtw.Close ();\r
112                          }\r
113                 }\r
114 \r
115                 void GetContentTypeProperties (string cts, out string encoding, out string content_type)\r
116                 {\r
117                         encoding = "utf-8";\r
118                         int start = 0;\r
119                         int idx = cts.IndexOf (';');\r
120                         if (idx == -1)\r
121                                 encoding = cts;\r
122                         content_type = cts.Substring (0, idx);\r
123                         for (start = idx + 1; idx != -1;){\r
124                                 idx = cts.IndexOf (";", start);\r
125                                 string body;\r
126                                 if (idx == -1)\r
127                                         body = cts.Substring (start);\r
128                                 else {\r
129                                         body = cts.Substring (start, idx);\r
130                                         start = idx + 1;\r
131                                 }\r
132                                 if (body.StartsWith ("charset=")){\r
133                                         encoding = body.Substring (8);\r
134                                 }\r
135                         }\r
136                 }\r
137                 \r
138                 //\r
139                 // TODO:\r
140                 //    Handle other web responses (multi-output?)\r
141                 //    \r
142                 object [] ReceiveResponse (WebResponse response, SoapClientMessage message)\r
143                 {\r
144                         HttpWebResponse http_response = (HttpWebResponse) response;\r
145                         HttpStatusCode code = http_response.StatusCode;\r
146                         MethodStubInfo msi = message.MethodStubInfo;\r
147 \r
148                         if (!(code == HttpStatusCode.Accepted || code == HttpStatusCode.OK || code == HttpStatusCode.InternalServerError))\r
149                                 throw new Exception ("Return code was: " + http_response.StatusCode);\r
150 \r
151                         //\r
152                         // Remove optional encoding\r
153 \r
154                         //\r
155                         string content_type, encoding_name;\r
156                         GetContentTypeProperties (response.ContentType, out encoding_name, out content_type);\r
157 \r
158                         if (content_type != "text/xml")\r
159                                 throw new Exception ("Return value is not XML: " + content_type);\r
160 \r
161                         Encoding encoding = Encoding.GetEncoding (encoding_name);\r
162                         Stream stream = response.GetResponseStream ();\r
163                         StreamReader reader = new StreamReader (stream, encoding, false);\r
164                         XmlTextReader xml_reader = new XmlTextReader (reader);\r
165 \r
166                         xml_reader.MoveToContent ();\r
167                         xml_reader.ReadStartElement ("Envelope", soap_envelope);\r
168                         xml_reader.MoveToContent ();\r
169                         xml_reader.ReadStartElement ("Body", soap_envelope);\r
170 \r
171                         if (code != HttpStatusCode.InternalServerError)\r
172                         {\r
173                                 object [] ret = (object []) msi.ResponseSerializer.Deserialize (xml_reader);\r
174                                 return (object []) ret;\r
175                         }\r
176                         else\r
177                         {\r
178                                 Fault fault = (Fault) msi.FaultSerializer.Deserialize (xml_reader);\r
179                                 throw new SoapException (fault.faultstring, fault.faultcode, fault.faultactor, fault.detail);\r
180                         }\r
181                 }\r
182 \r
183                 protected object[] Invoke (string method_name, object[] parameters)\r
184                 {\r
185                         MethodStubInfo msi = type_info.GetMethod (method_name);\r
186 \r
187                         SoapClientMessage message = new SoapClientMessage (\r
188                                 this, msi, Url, parameters);\r
189 \r
190                         WebResponse response;\r
191                         try\r
192                         {\r
193                                 WebRequest request = GetWebRequest (uri);\r
194                                 SendRequest (request, message);\r
195                                 response = request.GetResponse ();\r
196                         }\r
197                         catch (WebException ex)\r
198                         {\r
199                                 response = ex.Response;\r
200                                 HttpWebResponse http_response = response as HttpWebResponse;\r
201                                 if (http_response == null || http_response.StatusCode != HttpStatusCode.InternalServerError)\r
202                                         throw ex;\r
203                         }\r
204 \r
205                         return ReceiveResponse (response, message);\r
206                 }\r
207 \r
208                 #endregion // Methods\r
209         }\r
210 }\r