2 // System.Web.Services.Protocols.HttpSoapWebServiceHandler.cs
5 // Lluis Sanchez Gual (lluis@ximian.com)
7 // Copyright (C) Ximian, Inc. 2003
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37 using System.Reflection;
38 using System.Xml.Serialization;
39 using System.Web.Services.Description;
41 namespace System.Web.Services.Protocols
43 internal class HttpSoapWebServiceHandler: WebServiceHandler
45 SoapTypeStubInfo _typeStubInfo;
46 SoapExtension[] _extensionChainHighPrio;
47 SoapExtension[] _extensionChainMedPrio;
48 SoapExtension[] _extensionChainLowPrio;
49 SoapMethodStubInfo methodInfo;
50 SoapServerMessage requestMessage = null;
53 public HttpSoapWebServiceHandler (Type type): base (type)
55 _typeStubInfo = (SoapTypeStubInfo) TypeStubManager.GetTypeStub (ServiceType, "Soap");
58 public override bool IsReusable
63 internal override MethodStubInfo GetRequestMethod (HttpContext context)
67 requestMessage = DeserializeRequest (context.Request);
72 SerializeFault (context, requestMessage, ex);
77 public override void ProcessRequest (HttpContext context)
80 SoapServerMessage responseMessage = null;
84 if (requestMessage == null)
85 requestMessage = DeserializeRequest (context.Request);
87 responseMessage = Invoke (context, requestMessage);
88 SerializeResponse (context.Response, responseMessage);
92 SerializeFault (context, requestMessage, ex);
97 SoapServerMessage DeserializeRequest (HttpRequest request)
99 Stream stream = request.InputStream;
103 string soapAction = null;
105 Encoding encoding = WebServiceHelper.GetContentEncoding (request.ContentType, out ctype);
106 if (ctype != "text/xml")
107 throw new WebException ("Content is not XML: " + ctype);
109 server = CreateServerInstance ();
111 SoapServerMessage message = new SoapServerMessage (request, server, stream);
112 message.SetStage (SoapMessageStage.BeforeDeserialize);
113 message.ContentType = ctype;
114 message.ContentEncoding = encoding.WebName;
116 // If the routing style is SoapAction, then we can get the method information now
117 // and set it to the SoapMessage
119 if (_typeStubInfo.RoutingStyle == SoapServiceRoutingStyle.SoapAction)
121 soapAction = request.Headers ["SOAPAction"];
122 if (soapAction == null) throw new SoapException ("Missing SOAPAction header", SoapException.ClientFaultCode);
123 methodInfo = _typeStubInfo.GetMethodForSoapAction (soapAction);
124 if (methodInfo == null) throw new SoapException ("Server did not recognize the value of HTTP header SOAPAction: " + soapAction, SoapException.ClientFaultCode);
125 message.MethodStubInfo = methodInfo;
128 // Execute the high priority global extensions. Do not try to execute the medium and
129 // low priority extensions because if the routing style is RequestElement we still
130 // don't have method information
132 _extensionChainHighPrio = SoapExtension.CreateExtensionChain (_typeStubInfo.SoapExtensions[0]);
133 stream = SoapExtension.ExecuteChainStream (_extensionChainHighPrio, stream);
134 SoapExtension.ExecuteProcessMessage (_extensionChainHighPrio, message, false);
136 // If the routing style is RequestElement, try to get the method name from the
137 // stream processed by the high priority extensions
139 if (_typeStubInfo.RoutingStyle == SoapServiceRoutingStyle.RequestElement)
141 MemoryStream mstream;
142 byte[] buffer = null;
146 buffer = new byte [stream.Length];
147 for (int n=0; n<stream.Length;)
148 n += stream.Read (buffer, n, (int)stream.Length-n);
149 mstream = new MemoryStream (buffer);
153 buffer = new byte [500];
154 mstream = new MemoryStream ();
157 while ((len = stream.Read (buffer, 0, 500)) > 0)
158 mstream.Write (buffer, 0, len);
159 mstream.Position = 0;
160 buffer = mstream.ToArray ();
163 soapAction = ReadActionFromRequestElement (new MemoryStream (buffer), encoding);
166 methodInfo = (SoapMethodStubInfo) _typeStubInfo.GetMethod (soapAction);
167 message.MethodStubInfo = methodInfo;
170 // Whatever routing style we used, we should now have the method information.
171 // We can now notify the remaining extensions
173 if (methodInfo == null) throw new SoapException ("Method '" + soapAction + "' not defined in the web service '" + _typeStubInfo.LogicalType.WebServiceName + "'", SoapException.ClientFaultCode);
175 _extensionChainMedPrio = SoapExtension.CreateExtensionChain (methodInfo.SoapExtensions);
176 _extensionChainLowPrio = SoapExtension.CreateExtensionChain (_typeStubInfo.SoapExtensions[1]);
178 stream = SoapExtension.ExecuteChainStream (_extensionChainMedPrio, stream);
179 stream = SoapExtension.ExecuteChainStream (_extensionChainLowPrio, stream);
180 SoapExtension.ExecuteProcessMessage (_extensionChainMedPrio, message, false);
181 SoapExtension.ExecuteProcessMessage (_extensionChainLowPrio, message, false);
183 // Deserialize the request
185 StreamReader reader = new StreamReader (stream, encoding, false);
186 XmlTextReader xmlReader = new XmlTextReader (reader);
191 SoapHeaderCollection headers;
\r
192 WebServiceHelper.ReadSoapMessage (xmlReader, _typeStubInfo, methodInfo.Use, methodInfo.RequestSerializer, out content, out headers);
\r
193 message.InParameters = (object []) content;
194 message.SetHeaders (headers);
198 throw new SoapException ("Could not deserialize Soap message", SoapException.ClientFaultCode, ex);
201 // Notify the extensions after deserialization
203 message.SetStage (SoapMessageStage.AfterDeserialize);
204 SoapExtension.ExecuteProcessMessage (_extensionChainHighPrio, message, false);
205 SoapExtension.ExecuteProcessMessage (_extensionChainMedPrio, message, false);
206 SoapExtension.ExecuteProcessMessage (_extensionChainLowPrio, message, false);
214 string ReadActionFromRequestElement (Stream stream, Encoding encoding)
218 StreamReader reader = new StreamReader (stream, encoding, false);
219 XmlTextReader xmlReader = new XmlTextReader (reader);
221 xmlReader.MoveToContent ();
222 xmlReader.ReadStartElement ("Envelope", WebServiceHelper.SoapEnvelopeNamespace);
224 while (! (xmlReader.NodeType == XmlNodeType.Element && xmlReader.LocalName == "Body" && xmlReader.NamespaceURI == WebServiceHelper.SoapEnvelopeNamespace))
227 xmlReader.ReadStartElement ("Body", WebServiceHelper.SoapEnvelopeNamespace);
228 xmlReader.MoveToContent ();
230 return xmlReader.LocalName;
234 string errmsg = "The root element for the request could not be determined. ";
235 errmsg += "When RoutingStyle is set to RequestElement, SoapExtensions configured ";
236 errmsg += "via an attribute on the method cannot modify the request stream before it is read. ";
237 errmsg += "The extension must be configured via the SoapExtensionTypes element in web.config ";
238 errmsg += "or the request must arrive at the server as clear text.";
239 throw new SoapException (errmsg, SoapException.ServerFaultCode, ex);
243 void SerializeResponse (HttpResponse response, SoapServerMessage message)
245 SoapMethodStubInfo methodInfo = message.MethodStubInfo;
247 response.ContentType = "text/xml; charset=utf-8";
248 if (message.Exception != null) response.StatusCode = 500;
250 long contentLength = 0;
251 Stream outStream = null;
252 MemoryStream bufferStream = null;
253 Stream responseStream = response.OutputStream;
254 bool bufferResponse = (methodInfo == null || methodInfo.MethodAttribute.BufferResponse);
258 bufferStream = new MemoryStream ();
259 outStream = bufferStream;
262 outStream = responseStream;
266 // While serializing, process extensions in reverse order
270 outStream = SoapExtension.ExecuteChainStream (_extensionChainHighPrio, outStream);
271 outStream = SoapExtension.ExecuteChainStream (_extensionChainMedPrio, outStream);
272 outStream = SoapExtension.ExecuteChainStream (_extensionChainLowPrio, outStream);
274 message.SetStage (SoapMessageStage.BeforeSerialize);
275 SoapExtension.ExecuteProcessMessage (_extensionChainLowPrio, message, true);
276 SoapExtension.ExecuteProcessMessage (_extensionChainMedPrio, message, true);
277 SoapExtension.ExecuteProcessMessage (_extensionChainHighPrio, message, true);
280 XmlTextWriter xtw = WebServiceHelper.CreateXmlWriter (outStream);
282 if (message.Exception == null)
283 WebServiceHelper.WriteSoapMessage (xtw, _typeStubInfo, methodInfo.Use, methodInfo.ResponseSerializer, message.OutParameters, message.Headers);
285 WebServiceHelper.WriteSoapMessage (xtw, _typeStubInfo, SoapBindingUse.Literal, Fault.Serializer, new Fault (message.Exception), null);
289 message.SetStage (SoapMessageStage.AfterSerialize);
290 SoapExtension.ExecuteProcessMessage (_extensionChainLowPrio, message, true);
291 SoapExtension.ExecuteProcessMessage (_extensionChainMedPrio, message, true);
292 SoapExtension.ExecuteProcessMessage (_extensionChainHighPrio, message, true);
295 if (bufferStream != null) contentLength = bufferStream.Length;
300 // If the response is buffered, we can discard the response and
301 // serialize a new Fault message as response.
302 if (bufferResponse) throw ex;
304 // If it is not buffered, we can't rollback what has been sent,
305 // so we can only close the connection and return.
306 responseStream.Close ();
313 responseStream.Write (bufferStream.GetBuffer(), 0, (int) contentLength);
317 responseStream.Close ();
320 void SerializeFault (HttpContext context, SoapServerMessage requestMessage, Exception ex)
322 SoapException soex = ex as SoapException;
323 if (soex == null) soex = new SoapException (ex.Message, SoapException.ServerFaultCode, ex);
325 SoapServerMessage faultMessage;
326 if (requestMessage != null)
327 faultMessage = new SoapServerMessage (context.Request, soex, requestMessage.MethodStubInfo, requestMessage.Server, requestMessage.Stream);
329 faultMessage = new SoapServerMessage (context.Request, soex, null, null, null);
331 SerializeResponse (context.Response, faultMessage);
332 context.Response.End ();
336 private SoapServerMessage Invoke (HttpContext ctx, SoapServerMessage requestMessage)
338 SoapMethodStubInfo methodInfo = requestMessage.MethodStubInfo;
340 // Assign header values to web service members
342 requestMessage.UpdateHeaderValues (requestMessage.Server, methodInfo.Headers);
344 // Fill an array with the input parameters at the right position
346 object[] parameters = new object[methodInfo.MethodInfo.Parameters.Length];
347 ParameterInfo[] inParams = methodInfo.MethodInfo.InParameters;
348 for (int n=0; n<inParams.Length; n++)
349 parameters [inParams[n].Position] = requestMessage.InParameters [n];
355 object[] results = methodInfo.MethodInfo.Invoke (requestMessage.Server, parameters);
356 requestMessage.OutParameters = results;
358 catch (TargetInvocationException ex)
360 throw ex.InnerException;
363 // Check that headers with MustUnderstand flag have been understood
365 foreach (SoapHeader header in requestMessage.Headers)
367 if (header.MustUnderstand && !header.DidUnderstand)
368 throw new SoapHeaderException ("Header not understood: " + header.GetType(), SoapException.MustUnderstandFaultCode);
371 // Collect headers that must be sent to the client
372 requestMessage.CollectHeaders (requestMessage.Server, methodInfo.Headers, SoapHeaderDirection.Out);
374 return requestMessage;