HttpSoapWebServiceHandler.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. //
  2. // System.Web.Services.Protocols.HttpSoapWebServiceHandler.cs
  3. //
  4. // Author:
  5. // Lluis Sanchez Gual ([email protected])
  6. //
  7. // Copyright (C) Ximian, Inc. 2003
  8. //
  9. using System;
  10. using System.Net;
  11. using System.Web;
  12. using System.Xml;
  13. using System.Text;
  14. using System.IO;
  15. using System.Reflection;
  16. using System.Xml.Serialization;
  17. using System.Web.Services.Description;
  18. namespace System.Web.Services.Protocols
  19. {
  20. internal class HttpSoapWebServiceHandler: WebServiceHandler
  21. {
  22. SoapTypeStubInfo _typeStubInfo;
  23. SoapExtension[] _extensionChainHighPrio;
  24. SoapExtension[] _extensionChainMedPrio;
  25. SoapExtension[] _extensionChainLowPrio;
  26. public HttpSoapWebServiceHandler (Type type): base (type)
  27. {
  28. _typeStubInfo = (SoapTypeStubInfo) TypeStubManager.GetTypeStub (ServiceType, "Soap");
  29. }
  30. public override bool IsReusable
  31. {
  32. get { return false; }
  33. }
  34. public override void ProcessRequest (HttpContext context)
  35. {
  36. SoapServerMessage requestMessage = null;
  37. SoapServerMessage responseMessage = null;
  38. try
  39. {
  40. requestMessage = DeserializeRequest (context.Request);
  41. responseMessage = Invoke (requestMessage);
  42. SerializeResponse (context.Response, responseMessage);
  43. }
  44. catch (Exception ex)
  45. {
  46. SerializeFault (context, requestMessage, ex);
  47. return;
  48. }
  49. }
  50. SoapServerMessage DeserializeRequest (HttpRequest request)
  51. {
  52. Stream stream = request.InputStream;
  53. using (stream)
  54. {
  55. string soapAction = null;
  56. SoapMethodStubInfo methodInfo = null;
  57. string ctype;
  58. Encoding encoding = WebServiceHelper.GetContentEncoding (request.ContentType, out ctype);
  59. if (ctype != "text/xml")
  60. throw new WebException ("Content is not XML: " + ctype);
  61. object server = CreateServerInstance ();
  62. SoapServerMessage message = new SoapServerMessage (request, server, stream);
  63. message.SetStage (SoapMessageStage.BeforeDeserialize);
  64. // If the routing style is SoapAction, then we can get the method information now
  65. // and set it to the SoapMessage
  66. if (_typeStubInfo.RoutingStyle == SoapServiceRoutingStyle.SoapAction)
  67. {
  68. soapAction = request.Headers ["SOAPAction"];
  69. if (soapAction == null) throw new SoapException ("Missing SOAPAction header", SoapException.ClientFaultCode);
  70. methodInfo = _typeStubInfo.GetMethodForSoapAction (soapAction);
  71. if (methodInfo == null) throw new SoapException ("Server did not recognize the value of HTTP header SOAPAction: " + soapAction, SoapException.ClientFaultCode);
  72. message.MethodStubInfo = methodInfo;
  73. }
  74. // Execute the high priority global extensions. Do not try to execute the medium and
  75. // low priority extensions because if the routing style is RequestElement we still
  76. // don't have method information
  77. _extensionChainHighPrio = SoapExtension.CreateExtensionChain (_typeStubInfo.SoapExtensions[0]);
  78. stream = SoapExtension.ExecuteChainStream (_extensionChainHighPrio, stream);
  79. SoapExtension.ExecuteProcessMessage (_extensionChainHighPrio, message, false);
  80. // If the routing style is RequestElement, try to get the method name from the
  81. // stream processed by the high priority extensions
  82. if (_typeStubInfo.RoutingStyle == SoapServiceRoutingStyle.RequestElement)
  83. {
  84. MemoryStream mstream;
  85. byte[] buffer = null;
  86. if (stream.CanSeek)
  87. {
  88. buffer = new byte [stream.Length];
  89. for (int n=0; n<stream.Length;)
  90. n += stream.Read (buffer, n, (int)stream.Length-n);
  91. mstream = new MemoryStream (buffer);
  92. }
  93. else
  94. {
  95. buffer = new byte [500];
  96. mstream = new MemoryStream ();
  97. int len;
  98. while ((len = stream.Read (buffer, 0, 500)) > 0)
  99. mstream.Write (buffer, 0, len);
  100. mstream.Position = 0;
  101. buffer = mstream.ToArray ();
  102. }
  103. soapAction = ReadActionFromRequestElement (new MemoryStream (buffer), encoding);
  104. stream = mstream;
  105. methodInfo = (SoapMethodStubInfo) _typeStubInfo.GetMethod (soapAction);
  106. message.MethodStubInfo = methodInfo;
  107. }
  108. // Whatever routing style we used, we should now have the method information.
  109. // We can now notify the remaining extensions
  110. if (methodInfo == null) throw new SoapException ("Method '" + soapAction + "' not defined in the web service '" + _typeStubInfo.LogicalType.WebServiceName + "'", SoapException.ClientFaultCode);
  111. _extensionChainMedPrio = SoapExtension.CreateExtensionChain (methodInfo.SoapExtensions);
  112. _extensionChainLowPrio = SoapExtension.CreateExtensionChain (_typeStubInfo.SoapExtensions[1]);
  113. stream = SoapExtension.ExecuteChainStream (_extensionChainMedPrio, stream);
  114. stream = SoapExtension.ExecuteChainStream (_extensionChainLowPrio, stream);
  115. SoapExtension.ExecuteProcessMessage (_extensionChainMedPrio, message, false);
  116. SoapExtension.ExecuteProcessMessage (_extensionChainLowPrio, message, false);
  117. // Deserialize the request
  118. StreamReader reader = new StreamReader (stream, encoding, false);
  119. XmlTextReader xmlReader = new XmlTextReader (reader);
  120. try
  121. {
  122. object content;
  123. SoapHeaderCollection headers;
  124. WebServiceHelper.ReadSoapMessage (xmlReader, _typeStubInfo, methodInfo.Use, methodInfo.RequestSerializer, out content, out headers);
  125. message.InParameters = (object []) content;
  126. message.SetHeaders (headers);
  127. }
  128. catch (Exception ex)
  129. {
  130. throw new SoapException ("Could not deserialize Soap message", SoapException.ClientFaultCode, ex);
  131. }
  132. // Notify the extensions after deserialization
  133. message.SetStage (SoapMessageStage.AfterDeserialize);
  134. SoapExtension.ExecuteProcessMessage (_extensionChainHighPrio, message, false);
  135. SoapExtension.ExecuteProcessMessage (_extensionChainMedPrio, message, false);
  136. SoapExtension.ExecuteProcessMessage (_extensionChainLowPrio, message, false);
  137. xmlReader.Close ();
  138. return message;
  139. }
  140. }
  141. string ReadActionFromRequestElement (Stream stream, Encoding encoding)
  142. {
  143. try
  144. {
  145. StreamReader reader = new StreamReader (stream, encoding, false);
  146. XmlTextReader xmlReader = new XmlTextReader (reader);
  147. xmlReader.MoveToContent ();
  148. xmlReader.ReadStartElement ("Envelope", WebServiceHelper.SoapEnvelopeNamespace);
  149. while (! (xmlReader.NodeType == XmlNodeType.Element && xmlReader.LocalName == "Body" && xmlReader.NamespaceURI == WebServiceHelper.SoapEnvelopeNamespace))
  150. xmlReader.Skip ();
  151. xmlReader.ReadStartElement ("Body", WebServiceHelper.SoapEnvelopeNamespace);
  152. xmlReader.MoveToContent ();
  153. return xmlReader.LocalName;
  154. }
  155. catch (Exception ex)
  156. {
  157. string errmsg = "The root element for the request could not be determined. ";
  158. errmsg += "When RoutingStyle is set to RequestElement, SoapExtensions configured ";
  159. errmsg += "via an attribute on the method cannot modify the request stream before it is read. ";
  160. errmsg += "The extension must be configured via the SoapExtensionTypes element in web.config ";
  161. errmsg += "or the request must arrive at the server as clear text.";
  162. throw new SoapException (errmsg, SoapException.ServerFaultCode, ex);
  163. }
  164. }
  165. void SerializeResponse (HttpResponse response, SoapServerMessage message)
  166. {
  167. SoapMethodStubInfo methodInfo = message.MethodStubInfo;
  168. response.ContentType = "text/xml; charset=utf-8";
  169. if (message.Exception != null) response.StatusCode = 500;
  170. long contentLength = 0;
  171. Stream outStream = null;
  172. MemoryStream bufferStream = null;
  173. Stream responseStream = response.OutputStream;
  174. bool bufferResponse = (methodInfo == null || methodInfo.MethodAttribute.BufferResponse);
  175. if (bufferResponse)
  176. {
  177. bufferStream = new MemoryStream ();
  178. outStream = bufferStream;
  179. }
  180. else
  181. outStream = responseStream;
  182. try
  183. {
  184. // While serializing, process extensions in reverse order
  185. if (bufferResponse)
  186. {
  187. outStream = SoapExtension.ExecuteChainStream (_extensionChainHighPrio, outStream);
  188. outStream = SoapExtension.ExecuteChainStream (_extensionChainMedPrio, outStream);
  189. outStream = SoapExtension.ExecuteChainStream (_extensionChainLowPrio, outStream);
  190. message.SetStage (SoapMessageStage.BeforeSerialize);
  191. SoapExtension.ExecuteProcessMessage (_extensionChainLowPrio, message, true);
  192. SoapExtension.ExecuteProcessMessage (_extensionChainMedPrio, message, true);
  193. SoapExtension.ExecuteProcessMessage (_extensionChainHighPrio, message, true);
  194. }
  195. // What a waste of UTF8encoders, but it has to be thread safe.
  196. XmlTextWriter xtw = new XmlTextWriter (outStream, new UTF8Encoding (false));
  197. xtw.Formatting = Formatting.Indented; // TODO: remove formatting when code is stable
  198. if (message.Exception == null)
  199. WebServiceHelper.WriteSoapMessage (xtw, _typeStubInfo, methodInfo.Use, methodInfo.ResponseSerializer, message.OutParameters, message.Headers);
  200. else
  201. WebServiceHelper.WriteSoapMessage (xtw, _typeStubInfo, SoapBindingUse.Literal, _typeStubInfo.GetFaultSerializer(), new Fault (message.Exception), null);
  202. if (bufferResponse)
  203. {
  204. message.SetStage (SoapMessageStage.AfterSerialize);
  205. SoapExtension.ExecuteProcessMessage (_extensionChainLowPrio, message, true);
  206. SoapExtension.ExecuteProcessMessage (_extensionChainMedPrio, message, true);
  207. SoapExtension.ExecuteProcessMessage (_extensionChainHighPrio, message, true);
  208. }
  209. if (bufferStream != null) contentLength = bufferStream.Length;
  210. xtw.Close ();
  211. }
  212. catch (Exception ex)
  213. {
  214. // If the response is buffered, we can discard the response and
  215. // serialize a new Fault message as response.
  216. if (bufferResponse) throw ex;
  217. // If it is not buffered, we can't rollback what has been sent,
  218. // so we can only close the connection and return.
  219. responseStream.Close ();
  220. return;
  221. }
  222. try
  223. {
  224. if (bufferResponse)
  225. responseStream.Write (bufferStream.GetBuffer(), 0, (int) contentLength);
  226. }
  227. catch {}
  228. responseStream.Close ();
  229. }
  230. void SerializeFault (HttpContext context, SoapServerMessage requestMessage, Exception ex)
  231. {
  232. SoapException soex = ex as SoapException;
  233. if (soex == null) soex = new SoapException (ex.Message, SoapException.ServerFaultCode, ex);
  234. MethodStubInfo stubInfo;
  235. object server;
  236. Stream stream;
  237. SoapServerMessage faultMessage;
  238. if (requestMessage != null)
  239. faultMessage = new SoapServerMessage (context.Request, soex, requestMessage.MethodStubInfo, requestMessage.Server, requestMessage.Stream);
  240. else
  241. faultMessage = new SoapServerMessage (context.Request, soex, null, null, null);
  242. SerializeResponse (context.Response, faultMessage);
  243. return;
  244. }
  245. private SoapServerMessage Invoke (SoapServerMessage requestMessage)
  246. {
  247. SoapMethodStubInfo methodInfo = requestMessage.MethodStubInfo;
  248. // Assign header values to web service members
  249. requestMessage.UpdateHeaderValues (requestMessage.Server, methodInfo.Headers);
  250. // Fill an array with the input parameters at the right position
  251. object[] parameters = new object[methodInfo.MethodInfo.Parameters.Length];
  252. ParameterInfo[] inParams = methodInfo.MethodInfo.InParameters;
  253. for (int n=0; n<inParams.Length; n++)
  254. parameters [inParams[n].Position] = requestMessage.InParameters [n];
  255. // Invoke the method
  256. try
  257. {
  258. object[] results = methodInfo.MethodInfo.Invoke (requestMessage.Server, parameters);
  259. requestMessage.OutParameters = results;
  260. }
  261. catch (TargetInvocationException ex)
  262. {
  263. throw ex.InnerException;
  264. }
  265. // Check that headers with MustUnderstand flag have been understood
  266. foreach (SoapHeader header in requestMessage.Headers)
  267. {
  268. if (header.MustUnderstand && !header.DidUnderstand)
  269. throw new SoapHeaderException ("Header not understood: " + header.GetType(), SoapException.MustUnderstandFaultCode);
  270. }
  271. // Collect headers that must be sent to the client
  272. requestMessage.CollectHeaders (requestMessage.Server, methodInfo.Headers, SoapHeaderDirection.Out);
  273. return requestMessage;
  274. }
  275. }
  276. }