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;
52 public HttpSoapWebServiceHandler (Type type): base (type)
54 _typeStubInfo = (SoapTypeStubInfo) TypeStubManager.GetTypeStub (ServiceType, "Soap");
57 public override bool IsReusable
62 internal override MethodStubInfo GetRequestMethod (HttpContext context)
66 requestMessage = DeserializeRequest (context);
71 SerializeFault (context, requestMessage, ex);
76 public override void ProcessRequest (HttpContext context)
79 SoapServerMessage responseMessage = null;
83 if (requestMessage == null) {
84 requestMessage = DeserializeRequest (context);
87 if (methodInfo != null && methodInfo.OneWay) {
88 context.Response.BufferOutput = false;
89 context.Response.StatusCode = 202;
90 context.Response.Flush ();
91 context.Response.Close ();
92 Invoke (context, requestMessage);
94 responseMessage = Invoke (context, requestMessage);
95 SerializeResponse (context.Response, responseMessage);
100 if (methodInfo != null && methodInfo.OneWay) {
101 context.Response.StatusCode = 500;
102 context.Response.Flush ();
103 context.Response.Close ();
105 SerializeFault (context, requestMessage, ex);
109 IDisposable disp = requestMessage.Server as IDisposable;
110 requestMessage = null;
116 SoapServerMessage DeserializeRequest (HttpContext context)
118 HttpRequest request = context.Request;
119 Stream stream = request.InputStream;
123 string soapAction = null;
125 Encoding encoding = WebServiceHelper.GetContentEncoding (request.ContentType, out ctype);
127 if (ctype != "text/xml" && ctype != "application/soap+xml")
129 if (ctype != "text/xml")
131 throw new WebException ("Content is not XML: " + ctype);
133 object server = CreateServerInstance ();
135 SoapServerMessage message = new SoapServerMessage (request, server, stream);
136 message.SetStage (SoapMessageStage.BeforeDeserialize);
137 message.ContentType = ctype;
139 object soapVer = context.Items ["WebServiceSoapVersion"];
141 message.SetSoapVersion ((SoapProtocolVersion) soapVer);
144 // If the routing style is SoapAction, then we can get the method information now
145 // and set it to the SoapMessage
147 if (_typeStubInfo.RoutingStyle == SoapServiceRoutingStyle.SoapAction)
149 string headerName = message.IsSoap12 ? "action" : "SOAPAction";
150 soapAction = message.IsSoap12 ? WebServiceHelper.GetContextAction(request.ContentType) : request.Headers [headerName];
151 if (soapAction == null) {
152 if (!message.IsSoap12)
153 throw new SoapException ("Missing SOAPAction header", WebServiceHelper.ClientFaultCode (message.IsSoap12));
157 methodInfo = _typeStubInfo.GetMethodForSoapAction (soapAction);
158 if (methodInfo == null) throw new SoapException ("Server did not recognize the value of HTTP header " + headerName + ": " + soapAction, WebServiceHelper.ClientFaultCode (message.IsSoap12));
159 message.MethodStubInfo = methodInfo;
163 // Execute the high priority global extensions. Do not try to execute the medium and
164 // low priority extensions because if the routing style is RequestElement we still
165 // don't have method information
167 _extensionChainHighPrio = SoapExtension.CreateExtensionChain (_typeStubInfo.SoapExtensions[0]);
168 stream = SoapExtension.ExecuteChainStream (_extensionChainHighPrio, stream);
169 SoapExtension.ExecuteProcessMessage (_extensionChainHighPrio, message, stream, false);
171 // If the routing style is RequestElement, try to get the method name from the
172 // stream processed by the high priority extensions
174 if (_typeStubInfo.RoutingStyle == SoapServiceRoutingStyle.RequestElement || (message.IsSoap12 && soapAction == null))
176 MemoryStream mstream;
177 byte[] buffer = null;
181 buffer = new byte [stream.Length];
182 for (int n=0; n<stream.Length;)
183 n += stream.Read (buffer, n, (int)stream.Length-n);
184 mstream = new MemoryStream (buffer);
188 buffer = new byte [500];
189 mstream = new MemoryStream ();
192 while ((len = stream.Read (buffer, 0, 500)) > 0)
193 mstream.Write (buffer, 0, len);
194 mstream.Position = 0;
195 buffer = mstream.ToArray ();
198 soapAction = ReadActionFromRequestElement (new MemoryStream (buffer), encoding, message.IsSoap12);
201 methodInfo = (SoapMethodStubInfo) _typeStubInfo.GetMethod (soapAction);
202 message.MethodStubInfo = methodInfo;
205 // Whatever routing style we used, we should now have the method information.
206 // We can now notify the remaining extensions
208 if (methodInfo == null) throw new SoapException ("Method '" + soapAction + "' not defined in the web service '" + _typeStubInfo.LogicalType.WebServiceName + "'", WebServiceHelper.ClientFaultCode (message.IsSoap12));
210 _extensionChainMedPrio = SoapExtension.CreateExtensionChain (methodInfo.SoapExtensions);
211 _extensionChainLowPrio = SoapExtension.CreateExtensionChain (_typeStubInfo.SoapExtensions[1]);
213 stream = SoapExtension.ExecuteChainStream (_extensionChainMedPrio, stream);
214 stream = SoapExtension.ExecuteChainStream (_extensionChainLowPrio, stream);
215 SoapExtension.ExecuteProcessMessage (_extensionChainMedPrio, message, stream, false);
216 SoapExtension.ExecuteProcessMessage (_extensionChainLowPrio, message, stream, false);
218 // Deserialize the request
220 StreamReader reader = new StreamReader (stream, encoding, false);
221 XmlTextReader xmlReader = new XmlTextReader (reader);
226 SoapHeaderCollection headers;
227 WebServiceHelper.ReadSoapMessage (xmlReader, methodInfo, SoapHeaderDirection.In, message.IsSoap12, out content, out headers);
228 message.InParameters = (object []) content;
229 message.SetHeaders (headers);
233 throw new SoapException ("Could not deserialize Soap message", WebServiceHelper.ClientFaultCode (message.IsSoap12), ex);
236 // Notify the extensions after deserialization
238 message.SetStage (SoapMessageStage.AfterDeserialize);
239 SoapExtension.ExecuteProcessMessage (_extensionChainHighPrio, message, stream, false);
240 SoapExtension.ExecuteProcessMessage (_extensionChainMedPrio, message, stream, false);
241 SoapExtension.ExecuteProcessMessage (_extensionChainLowPrio, message, stream, false);
247 string ReadActionFromRequestElement (Stream stream, Encoding encoding, bool soap12)
249 string envNS = soap12 ?
250 WebServiceHelper.Soap12EnvelopeNamespace :
251 WebServiceHelper.SoapEnvelopeNamespace;
254 StreamReader reader = new StreamReader (stream, encoding, false);
255 XmlTextReader xmlReader = new XmlTextReader (reader);
257 xmlReader.MoveToContent ();
258 xmlReader.ReadStartElement ("Envelope", envNS);
260 while (! (xmlReader.NodeType == XmlNodeType.Element && xmlReader.LocalName == "Body" && xmlReader.NamespaceURI == envNS))
263 xmlReader.ReadStartElement ("Body", envNS);
264 xmlReader.MoveToContent ();
266 return xmlReader.LocalName;
270 string errmsg = "The root element for the request could not be determined. ";
271 errmsg += "When RoutingStyle is set to RequestElement, SoapExtensions configured ";
272 errmsg += "via an attribute on the method cannot modify the request stream before it is read. ";
273 errmsg += "The extension must be configured via the SoapExtensionTypes element in web.config ";
274 errmsg += "or the request must arrive at the server as clear text.";
275 throw new SoapException (errmsg, WebServiceHelper.ServerFaultCode (soap12), ex);
279 void SerializeResponse (HttpResponse response, SoapServerMessage message)
281 SoapMethodStubInfo methodInfo = message.MethodStubInfo;
283 if ((message.ContentEncoding != null) && (message.ContentEncoding.Length > 0))
284 response.AppendHeader("Content-Encoding", message.ContentEncoding);
286 response.ContentType = message.IsSoap12 ?
287 "application/soap+xml; charset=utf-8" :
288 "text/xml; charset=utf-8";
289 if (message.Exception != null) response.StatusCode = 500;
291 Stream responseStream = response.OutputStream;
292 Stream outStream = responseStream;
293 bool bufferResponse = (methodInfo == null || methodInfo.MethodAttribute.BufferResponse);
294 response.BufferOutput = bufferResponse;
298 // While serializing, process extensions in reverse order
302 outStream = SoapExtension.ExecuteChainStream (_extensionChainHighPrio, outStream);
303 outStream = SoapExtension.ExecuteChainStream (_extensionChainMedPrio, outStream);
304 outStream = SoapExtension.ExecuteChainStream (_extensionChainLowPrio, outStream);
306 message.SetStage (SoapMessageStage.BeforeSerialize);
307 SoapExtension.ExecuteProcessMessage (_extensionChainLowPrio, message, outStream, true);
308 SoapExtension.ExecuteProcessMessage (_extensionChainMedPrio, message, outStream, true);
309 SoapExtension.ExecuteProcessMessage (_extensionChainHighPrio, message, outStream, true);
312 XmlTextWriter xtw = WebServiceHelper.CreateXmlWriter (outStream);
315 if (message.Exception == null)
316 WebServiceHelper.WriteSoapMessage (xtw, methodInfo, SoapHeaderDirection.Out, message.OutParameters, message.Headers, message.IsSoap12);
317 else if (methodInfo != null) {
319 if (message.IsSoap12)
320 WebServiceHelper.WriteSoapMessage (xtw, methodInfo, SoapHeaderDirection.Fault, new Soap12Fault (message.Exception), message.Headers, message.IsSoap12);
324 WebServiceHelper.WriteSoapMessage (xtw, methodInfo, SoapHeaderDirection.Fault, new Fault (message.Exception), message.Headers, message.IsSoap12);
329 if (message.IsSoap12)
330 WebServiceHelper.WriteSoapMessage (xtw, SoapBindingUse.Literal, Soap12Fault.Serializer, null, new Soap12Fault (message.Exception), null, message.IsSoap12);
334 WebServiceHelper.WriteSoapMessage (xtw, SoapBindingUse.Literal, Fault.Serializer, null, new Fault (message.Exception), null, message.IsSoap12);
340 message.SetStage (SoapMessageStage.AfterSerialize);
341 SoapExtension.ExecuteProcessMessage (_extensionChainLowPrio, message, outStream, true);
342 SoapExtension.ExecuteProcessMessage (_extensionChainMedPrio, message, outStream, true);
343 SoapExtension.ExecuteProcessMessage (_extensionChainHighPrio, message, outStream, true);
350 // If the response is buffered, we can discard the response and
351 // serialize a new Fault message as response.
352 if (bufferResponse) throw ex;
354 // If it is not buffered, we can't rollback what has been sent,
355 // so we can only close the connection and return.
356 responseStream.Close ();
361 void SerializeFault (HttpContext context, SoapServerMessage requestMessage, Exception ex)
363 SoapException soex = ex as SoapException;
364 if (soex == null) soex = new SoapException (ex.Message, WebServiceHelper.ServerFaultCode (requestMessage != null && requestMessage.IsSoap12), ex);
366 SoapServerMessage faultMessage;
367 if (requestMessage != null)
368 faultMessage = new SoapServerMessage (context.Request, soex, requestMessage.MethodStubInfo, requestMessage.Server, requestMessage.Stream);
370 faultMessage = new SoapServerMessage (context.Request, soex, null, null, null);
372 object soapVer = context.Items ["WebServiceSoapVersion"];
374 faultMessage.SetSoapVersion ((SoapProtocolVersion) soapVer);
377 SerializeResponse (context.Response, faultMessage);
378 context.Response.End ();
382 private SoapServerMessage Invoke (HttpContext ctx, SoapServerMessage requestMessage)
384 SoapMethodStubInfo methodInfo = requestMessage.MethodStubInfo;
386 // Assign header values to web service members
388 requestMessage.UpdateHeaderValues (requestMessage.Server, methodInfo.Headers);
390 // Fill an array with the input parameters at the right position
392 object[] parameters = new object[methodInfo.MethodInfo.Parameters.Length];
393 ParameterInfo[] inParams = methodInfo.MethodInfo.InParameters;
394 for (int n=0; n<inParams.Length; n++)
395 parameters [inParams[n].Position] = requestMessage.InParameters [n];
401 object[] results = methodInfo.MethodInfo.Invoke (requestMessage.Server, parameters);
402 requestMessage.OutParameters = results;
404 catch (TargetInvocationException ex)
406 throw ex.InnerException;
409 // Check that headers with MustUnderstand flag have been understood
411 foreach (SoapHeader header in requestMessage.Headers)
413 if (header.MustUnderstand && !header.DidUnderstand)
414 throw new SoapHeaderException ("Header not understood: " + header.GetType(), WebServiceHelper.MustUnderstandFaultCode (requestMessage.IsSoap12));
417 // Collect headers that must be sent to the client
418 requestMessage.CollectHeaders (requestMessage.Server, methodInfo.Headers, SoapHeaderDirection.Out);
420 return requestMessage;