2003-06-21 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[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                         return base.GetWebRequest (uri);\r
59                 }\r
60 \r
61                 //\r
62                 // Just for debugging\r
63                 //\r
64                 void DumpStackFrames ()\r
65                 {\r
66                         StackTrace st = new StackTrace ();\r
67 \r
68                         for (int i = 0; i < st.FrameCount; i++){\r
69                                 StackFrame sf = st.GetFrame (i);\r
70                                 Console.WriteLine ("At frame: {0} {1}", i, sf.GetMethod ().Name);\r
71                         }\r
72                 }\r
73                 \r
74                 const string soap_envelope = "http://schemas.xmlsoap.org/soap/envelope/";\r
75                 \r
76                 void WriteSoapEnvelope (XmlTextWriter xtw, SoapHeaderCollection headers)\r
77                 {\r
78                         xtw.WriteStartDocument ();\r
79                         \r
80                         xtw.WriteStartElement ("soap", "Envelope", soap_envelope);\r
81                         xtw.WriteAttributeString ("xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance");\r
82                         xtw.WriteAttributeString ("xmlns", "xsd", null, "http://www.w3.org/2001/XMLSchema");\r
83 \r
84                         foreach (SoapHeader header in headers) {\r
85                                 XmlSerializer ser = type_info.GetCommonSerializer (header.GetType());\r
86                         xtw.WriteStartElement ("soap", "Header", soap_envelope);\r
87                                 ser.Serialize (xtw, header);\r
88                         xtw.WriteEndElement ();\r
89                         }\r
90                 }\r
91 \r
92                 void SendRequest (WebRequest request, SoapClientMessage message)\r
93                 {\r
94                         request.Method = "POST";\r
95                         WebHeaderCollection headers = request.Headers;\r
96                         headers.Add ("SOAPAction", message.Action);\r
97 \r
98                         request.ContentType = message.ContentType + "; charset=utf-8";\r
99 \r
100                         using (Stream s = request.GetRequestStream ()){\r
101                                 // serialize arguments\r
102 \r
103                                 // What a waste of UTF8encoders, but it has to be thread safe.\r
104                                 \r
105                                 XmlTextWriter xtw = new XmlTextWriter (s, new UTF8Encoding (false));\r
106                                 xtw.Formatting = Formatting.Indented;\r
107                                 WriteSoapEnvelope (xtw, message.Headers);\r
108                                 xtw.WriteStartElement ("soap", "Body", soap_envelope);\r
109 \r
110                                 // Serialize arguments.\r
111                                 message.MethodStubInfo.RequestSerializer.Serialize (xtw, message.Parameters);\r
112                                 xtw.WriteEndElement ();\r
113                                 xtw.WriteEndElement ();\r
114                                 \r
115                                 xtw.Flush ();\r
116                                 xtw.Close ();\r
117                          }\r
118                 }\r
119 \r
120                 void GetContentTypeProperties (string cts, out string encoding, out string content_type)\r
121                 {\r
122                         encoding = "utf-8";\r
123                         int start = 0;\r
124                         int idx = cts.IndexOf (';');\r
125                         if (idx == -1)\r
126                                 encoding = cts;\r
127                         content_type = cts.Substring (0, idx);\r
128                         for (start = idx + 1; idx != -1;){\r
129                                 idx = cts.IndexOf (";", start);\r
130                                 string body;\r
131                                 if (idx == -1)\r
132                                         body = cts.Substring (start);\r
133                                 else {\r
134                                         body = cts.Substring (start, idx);\r
135                                         start = idx + 1;\r
136                                 }\r
137                                 if (body.StartsWith ("charset=")){\r
138                                         encoding = body.Substring (8);\r
139                                 }\r
140                         }\r
141                 }\r
142                 \r
143                 //\r
144                 // TODO:\r
145                 //    Handle other web responses (multi-output?)\r
146                 //    \r
147                 object [] ReceiveResponse (WebResponse response, SoapClientMessage message)\r
148                 {\r
149                         HttpWebResponse http_response = (HttpWebResponse) response;\r
150                         HttpStatusCode code = http_response.StatusCode;\r
151                         MethodStubInfo msi = message.MethodStubInfo;\r
152 \r
153                         if (!(code == HttpStatusCode.Accepted || code == HttpStatusCode.OK || code == HttpStatusCode.InternalServerError))\r
154                                 throw new Exception ("Return code was: " + http_response.StatusCode);\r
155 \r
156                         //\r
157                         // Remove optional encoding\r
158 \r
159                         //\r
160                         string content_type, encoding_name;\r
161                         GetContentTypeProperties (response.ContentType, out encoding_name, out content_type);\r
162 \r
163                         if (content_type != "text/xml")\r
164                                 throw new Exception ("Return value is not XML: " + content_type);\r
165 \r
166                         Encoding encoding = Encoding.GetEncoding (encoding_name);\r
167                         Stream stream = response.GetResponseStream ();\r
168                         StreamReader reader = new StreamReader (stream, encoding, false);\r
169                         XmlTextReader xml_reader = new XmlTextReader (reader);\r
170 \r
171                         xml_reader.MoveToContent ();\r
172                         xml_reader.ReadStartElement ("Envelope", soap_envelope);\r
173                         xml_reader.MoveToContent ();\r
174                         xml_reader.ReadStartElement ("Body", soap_envelope);\r
175 \r
176                         if (code != HttpStatusCode.InternalServerError)\r
177                         {\r
178                                 object [] ret = (object []) msi.ResponseSerializer.Deserialize (xml_reader);\r
179                                 return (object []) ret;\r
180                         }\r
181                         else\r
182                         {\r
183                                 Fault fault = (Fault) type_info.FaultSerializer.Deserialize (xml_reader);\r
184                                 throw new SoapException (fault.faultstring, fault.faultcode, fault.faultactor, fault.detail);\r
185                         }\r
186                 }\r
187 \r
188                 protected object[] Invoke (string method_name, object[] parameters)\r
189                 {\r
190                         MethodStubInfo msi = type_info.GetMethod (method_name);\r
191 \r
192                         SoapClientMessage message = new SoapClientMessage (\r
193                                 this, msi, Url, parameters);\r
194 \r
195                         WebResponse response;\r
196                         try\r
197                         {\r
198                                 WebRequest request = GetWebRequest (uri);\r
199                                 SendRequest (request, message);\r
200                                 response = GetWebResponse (request);\r
201                         }\r
202                         catch (WebException ex)\r
203                         {\r
204                                 response = ex.Response;\r
205                                 HttpWebResponse http_response = response as HttpWebResponse;\r
206                                 if (http_response == null || http_response.StatusCode != HttpStatusCode.InternalServerError)\r
207                                         throw ex;\r
208                         }\r
209 \r
210                         return ReceiveResponse (response, message);\r
211                 }\r
212 \r
213                 #endregion // Methods\r
214         }\r
215 }\r