| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382 |
- //
- // System.Web.Services.Protocols.HttpSoapWebServiceHandler.cs
- //
- // Author:
- // Lluis Sanchez Gual ([email protected])
- //
- // Copyright (C) Ximian, Inc. 2003
- //
- //
- // Permission is hereby granted, free of charge, to any person obtaining
- // a copy of this software and associated documentation files (the
- // "Software"), to deal in the Software without restriction, including
- // without limitation the rights to use, copy, modify, merge, publish,
- // distribute, sublicense, and/or sell copies of the Software, and to
- // permit persons to whom the Software is furnished to do so, subject to
- // the following conditions:
- //
- // The above copyright notice and this permission notice shall be
- // included in all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- //
- using System;
- using System.Net;
- using System.Web;
- using System.Xml;
- using System.Text;
- using System.IO;
- using System.Reflection;
- using System.Xml.Serialization;
- using System.Web.Services.Description;
- namespace System.Web.Services.Protocols
- {
- internal class HttpSoapWebServiceHandler: WebServiceHandler
- {
- SoapTypeStubInfo _typeStubInfo;
- SoapExtension[] _extensionChainHighPrio;
- SoapExtension[] _extensionChainMedPrio;
- SoapExtension[] _extensionChainLowPrio;
- SoapMethodStubInfo methodInfo;
- SoapServerMessage requestMessage = null;
- object server;
- public HttpSoapWebServiceHandler (Type type): base (type)
- {
- _typeStubInfo = (SoapTypeStubInfo) TypeStubManager.GetTypeStub (ServiceType, "Soap");
- }
- public override bool IsReusable
- {
- get { return false; }
- }
- internal override MethodStubInfo GetRequestMethod (HttpContext context)
- {
- try
- {
- requestMessage = DeserializeRequest (context.Request);
- return methodInfo;
- }
- catch (Exception ex)
- {
- SerializeFault (context, requestMessage, ex);
- return null;
- }
- }
- public override void ProcessRequest (HttpContext context)
- {
- Context = context;
- SoapServerMessage responseMessage = null;
- try
- {
- if (requestMessage == null)
- requestMessage = DeserializeRequest (context.Request);
-
- responseMessage = Invoke (context, requestMessage);
- SerializeResponse (context.Response, responseMessage);
- }
- catch (Exception ex)
- {
- SerializeFault (context, requestMessage, ex);
- return;
- }
- }
- SoapServerMessage DeserializeRequest (HttpRequest request)
- {
- Stream stream = request.InputStream;
- using (stream)
- {
- string soapAction = null;
- string ctype;
- Encoding encoding = WebServiceHelper.GetContentEncoding (request.ContentType, out ctype);
- if (ctype != "text/xml")
- throw new WebException ("Content is not XML: " + ctype);
-
- server = CreateServerInstance ();
- SoapServerMessage message = new SoapServerMessage (request, server, stream);
- message.SetStage (SoapMessageStage.BeforeDeserialize);
- message.ContentType = ctype;
- message.ContentEncoding = encoding.WebName;
- // If the routing style is SoapAction, then we can get the method information now
- // and set it to the SoapMessage
- if (_typeStubInfo.RoutingStyle == SoapServiceRoutingStyle.SoapAction)
- {
- soapAction = request.Headers ["SOAPAction"];
- if (soapAction == null) throw new SoapException ("Missing SOAPAction header", SoapException.ClientFaultCode);
- methodInfo = _typeStubInfo.GetMethodForSoapAction (soapAction);
- if (methodInfo == null) throw new SoapException ("Server did not recognize the value of HTTP header SOAPAction: " + soapAction, SoapException.ClientFaultCode);
- message.MethodStubInfo = methodInfo;
- }
- // Execute the high priority global extensions. Do not try to execute the medium and
- // low priority extensions because if the routing style is RequestElement we still
- // don't have method information
- _extensionChainHighPrio = SoapExtension.CreateExtensionChain (_typeStubInfo.SoapExtensions[0]);
- stream = SoapExtension.ExecuteChainStream (_extensionChainHighPrio, stream);
- SoapExtension.ExecuteProcessMessage (_extensionChainHighPrio, message, false);
- // If the routing style is RequestElement, try to get the method name from the
- // stream processed by the high priority extensions
- if (_typeStubInfo.RoutingStyle == SoapServiceRoutingStyle.RequestElement)
- {
- MemoryStream mstream;
- byte[] buffer = null;
- if (stream.CanSeek)
- {
- buffer = new byte [stream.Length];
- for (int n=0; n<stream.Length;)
- n += stream.Read (buffer, n, (int)stream.Length-n);
- mstream = new MemoryStream (buffer);
- }
- else
- {
- buffer = new byte [500];
- mstream = new MemoryStream ();
-
- int len;
- while ((len = stream.Read (buffer, 0, 500)) > 0)
- mstream.Write (buffer, 0, len);
- mstream.Position = 0;
- buffer = mstream.ToArray ();
- }
- soapAction = ReadActionFromRequestElement (new MemoryStream (buffer), encoding);
- stream = mstream;
- methodInfo = (SoapMethodStubInfo) _typeStubInfo.GetMethod (soapAction);
- message.MethodStubInfo = methodInfo;
- }
- // Whatever routing style we used, we should now have the method information.
- // We can now notify the remaining extensions
- if (methodInfo == null) throw new SoapException ("Method '" + soapAction + "' not defined in the web service '" + _typeStubInfo.LogicalType.WebServiceName + "'", SoapException.ClientFaultCode);
- _extensionChainMedPrio = SoapExtension.CreateExtensionChain (methodInfo.SoapExtensions);
- _extensionChainLowPrio = SoapExtension.CreateExtensionChain (_typeStubInfo.SoapExtensions[1]);
- stream = SoapExtension.ExecuteChainStream (_extensionChainMedPrio, stream);
- stream = SoapExtension.ExecuteChainStream (_extensionChainLowPrio, stream);
- SoapExtension.ExecuteProcessMessage (_extensionChainMedPrio, message, false);
- SoapExtension.ExecuteProcessMessage (_extensionChainLowPrio, message, false);
- // Deserialize the request
- StreamReader reader = new StreamReader (stream, encoding, false);
- XmlTextReader xmlReader = new XmlTextReader (reader);
- try
- {
- object content;
- SoapHeaderCollection headers;
- WebServiceHelper.ReadSoapMessage (xmlReader, _typeStubInfo, methodInfo.Use, methodInfo.RequestSerializer, out content, out headers);
- message.InParameters = (object []) content;
- message.SetHeaders (headers);
- }
- catch (Exception ex)
- {
- throw new SoapException ("Could not deserialize Soap message", SoapException.ClientFaultCode, ex);
- }
- // Notify the extensions after deserialization
- message.SetStage (SoapMessageStage.AfterDeserialize);
- SoapExtension.ExecuteProcessMessage (_extensionChainHighPrio, message, false);
- SoapExtension.ExecuteProcessMessage (_extensionChainMedPrio, message, false);
- SoapExtension.ExecuteProcessMessage (_extensionChainLowPrio, message, false);
- xmlReader.Close ();
- return message;
- }
- }
- string ReadActionFromRequestElement (Stream stream, Encoding encoding)
- {
- try
- {
- StreamReader reader = new StreamReader (stream, encoding, false);
- XmlTextReader xmlReader = new XmlTextReader (reader);
- xmlReader.MoveToContent ();
- xmlReader.ReadStartElement ("Envelope", WebServiceHelper.SoapEnvelopeNamespace);
- while (! (xmlReader.NodeType == XmlNodeType.Element && xmlReader.LocalName == "Body" && xmlReader.NamespaceURI == WebServiceHelper.SoapEnvelopeNamespace))
- xmlReader.Skip ();
- xmlReader.ReadStartElement ("Body", WebServiceHelper.SoapEnvelopeNamespace);
- xmlReader.MoveToContent ();
- return xmlReader.LocalName;
- }
- catch (Exception ex)
- {
- string errmsg = "The root element for the request could not be determined. ";
- errmsg += "When RoutingStyle is set to RequestElement, SoapExtensions configured ";
- errmsg += "via an attribute on the method cannot modify the request stream before it is read. ";
- errmsg += "The extension must be configured via the SoapExtensionTypes element in web.config ";
- errmsg += "or the request must arrive at the server as clear text.";
- throw new SoapException (errmsg, SoapException.ServerFaultCode, ex);
- }
- }
- void SerializeResponse (HttpResponse response, SoapServerMessage message)
- {
- SoapMethodStubInfo methodInfo = message.MethodStubInfo;
-
- response.ContentType = "text/xml; charset=utf-8";
- if (message.Exception != null) response.StatusCode = 500;
- long contentLength = 0;
- Stream outStream = null;
- MemoryStream bufferStream = null;
- Stream responseStream = response.OutputStream;
- bool bufferResponse = (methodInfo == null || methodInfo.MethodAttribute.BufferResponse);
-
- if (bufferResponse)
- {
- bufferStream = new MemoryStream ();
- outStream = bufferStream;
- }
- else
- outStream = responseStream;
- try
- {
- // While serializing, process extensions in reverse order
- if (bufferResponse)
- {
- outStream = SoapExtension.ExecuteChainStream (_extensionChainHighPrio, outStream);
- outStream = SoapExtension.ExecuteChainStream (_extensionChainMedPrio, outStream);
- outStream = SoapExtension.ExecuteChainStream (_extensionChainLowPrio, outStream);
-
- message.SetStage (SoapMessageStage.BeforeSerialize);
- SoapExtension.ExecuteProcessMessage (_extensionChainLowPrio, message, true);
- SoapExtension.ExecuteProcessMessage (_extensionChainMedPrio, message, true);
- SoapExtension.ExecuteProcessMessage (_extensionChainHighPrio, message, true);
- }
-
- XmlTextWriter xtw = WebServiceHelper.CreateXmlWriter (outStream);
-
- if (message.Exception == null)
- WebServiceHelper.WriteSoapMessage (xtw, _typeStubInfo, methodInfo.Use, methodInfo.ResponseSerializer, message.OutParameters, message.Headers);
- else
- WebServiceHelper.WriteSoapMessage (xtw, _typeStubInfo, SoapBindingUse.Literal, Fault.Serializer, new Fault (message.Exception), null);
- if (bufferResponse)
- {
- message.SetStage (SoapMessageStage.AfterSerialize);
- SoapExtension.ExecuteProcessMessage (_extensionChainLowPrio, message, true);
- SoapExtension.ExecuteProcessMessage (_extensionChainMedPrio, message, true);
- SoapExtension.ExecuteProcessMessage (_extensionChainHighPrio, message, true);
- }
-
- if (bufferStream != null) contentLength = bufferStream.Length;
- xtw.Close ();
- }
- catch (Exception ex)
- {
- // If the response is buffered, we can discard the response and
- // serialize a new Fault message as response.
- if (bufferResponse) throw ex;
-
- // If it is not buffered, we can't rollback what has been sent,
- // so we can only close the connection and return.
- responseStream.Close ();
- return;
- }
-
- try
- {
- if (bufferResponse)
- responseStream.Write (bufferStream.GetBuffer(), 0, (int) contentLength);
- }
- catch {}
-
- responseStream.Close ();
- }
- void SerializeFault (HttpContext context, SoapServerMessage requestMessage, Exception ex)
- {
- SoapException soex = ex as SoapException;
- if (soex == null) soex = new SoapException (ex.Message, SoapException.ServerFaultCode, ex);
- SoapServerMessage faultMessage;
- if (requestMessage != null)
- faultMessage = new SoapServerMessage (context.Request, soex, requestMessage.MethodStubInfo, requestMessage.Server, requestMessage.Stream);
- else
- faultMessage = new SoapServerMessage (context.Request, soex, null, null, null);
- SerializeResponse (context.Response, faultMessage);
- context.Response.End ();
- return;
- }
-
- private SoapServerMessage Invoke (HttpContext ctx, SoapServerMessage requestMessage)
- {
- WebService wsi = requestMessage.Server as WebService;
- if (wsi != null) {
- wsi.SetContext (ctx);
- }
-
- SoapMethodStubInfo methodInfo = requestMessage.MethodStubInfo;
- // Assign header values to web service members
- requestMessage.UpdateHeaderValues (requestMessage.Server, methodInfo.Headers);
- // Fill an array with the input parameters at the right position
- object[] parameters = new object[methodInfo.MethodInfo.Parameters.Length];
- ParameterInfo[] inParams = methodInfo.MethodInfo.InParameters;
- for (int n=0; n<inParams.Length; n++)
- parameters [inParams[n].Position] = requestMessage.InParameters [n];
- // Invoke the method
- try
- {
- object[] results = methodInfo.MethodInfo.Invoke (requestMessage.Server, parameters);
- requestMessage.OutParameters = results;
- }
- catch (TargetInvocationException ex)
- {
- throw ex.InnerException;
- }
- // Check that headers with MustUnderstand flag have been understood
-
- foreach (SoapHeader header in requestMessage.Headers)
- {
- if (header.MustUnderstand && !header.DidUnderstand)
- throw new SoapHeaderException ("Header not understood: " + header.GetType(), SoapException.MustUnderstandFaultCode);
- }
- // Collect headers that must be sent to the client
- requestMessage.CollectHeaders (requestMessage.Server, methodInfo.Headers, SoapHeaderDirection.Out);
- return requestMessage;
- }
- }
- }
|