| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415 |
- //
- // 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;
- 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);
- #if NET_2_0
- object soapVer = context.Items ["WebServiceSoapVersion"];
- if (soapVer != null)
- requestMessage.SetSoapVersion ((SoapProtocolVersion) soapVer);
- #endif
- }
-
- if (methodInfo != null && methodInfo.OneWay) {
- context.Response.BufferOutput = false;
- context.Response.StatusCode = 202;
- context.Response.Flush ();
- context.Response.Close ();
- Invoke (context, requestMessage);
- } else {
- responseMessage = Invoke (context, requestMessage);
- SerializeResponse (context.Response, responseMessage);
- }
- }
- catch (Exception ex)
- {
- if (methodInfo != null && methodInfo.OneWay) {
- context.Response.StatusCode = 500;
- context.Response.Flush ();
- context.Response.Close ();
- } else {
- SerializeFault (context, requestMessage, ex);
- }
- }
- finally {
- IDisposable disp = requestMessage.Server as IDisposable;
- requestMessage = null;
- if (disp != null)
- disp.Dispose();
- }
- }
- SoapServerMessage DeserializeRequest (HttpRequest request)
- {
- Stream stream = request.InputStream;
- //using (stream)
- //{
- string soapAction = null;
- string ctype;
- Encoding encoding = WebServiceHelper.GetContentEncoding (request.ContentType, out ctype);
- #if NET_2_0
- if (ctype != "text/xml" && ctype != "application/soap+xml")
- #else
- if (ctype != "text/xml")
- #endif
- throw new WebException ("Content is not XML: " + ctype);
-
- object server = CreateServerInstance ();
- SoapServerMessage message = new SoapServerMessage (request, server, stream);
- message.SetStage (SoapMessageStage.BeforeDeserialize);
- message.ContentType = ctype;
- // 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, stream, 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, stream, false);
- SoapExtension.ExecuteProcessMessage (_extensionChainLowPrio, message, stream, false);
- // Deserialize the request
- StreamReader reader = new StreamReader (stream, encoding, false);
- XmlTextReader xmlReader = new XmlTextReader (reader);
- try
- {
- object content;
- SoapHeaderCollection headers;
- WebServiceHelper.ReadSoapMessage (xmlReader, methodInfo, SoapHeaderDirection.In, 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, stream, false);
- SoapExtension.ExecuteProcessMessage (_extensionChainMedPrio, message, stream, false);
- SoapExtension.ExecuteProcessMessage (_extensionChainLowPrio, message, stream, false);
- 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;
-
- if ((message.ContentEncoding != null) && (message.ContentEncoding.Length > 0))
- response.AppendHeader("Content-Encoding", message.ContentEncoding);
- response.ContentType = "text/xml; charset=utf-8";
- if (message.Exception != null) response.StatusCode = 500;
- Stream responseStream = response.OutputStream;
- Stream outStream = responseStream;
- bool bufferResponse = (methodInfo == null || methodInfo.MethodAttribute.BufferResponse);
- response.BufferOutput = bufferResponse;
- 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, outStream, true);
- SoapExtension.ExecuteProcessMessage (_extensionChainMedPrio, message, outStream, true);
- SoapExtension.ExecuteProcessMessage (_extensionChainHighPrio, message, outStream, true);
- }
-
- XmlTextWriter xtw = WebServiceHelper.CreateXmlWriter (outStream);
- #if NET_2_0
- bool soap12 = message.SoapVersion == SoapProtocolVersion.Soap12;
- #else
- bool soap12 = false;
- #endif
- if (message.Exception == null)
- WebServiceHelper.WriteSoapMessage (xtw, methodInfo, SoapHeaderDirection.Out, message.OutParameters, message.Headers, soap12);
- else if (methodInfo != null) {
- #if NET_2_0
- if (soap12)
- WebServiceHelper.WriteSoapMessage (xtw, methodInfo, SoapHeaderDirection.Fault, new Soap12Fault (message.Exception), message.Headers, soap12);
- else
- #endif
- {
- WebServiceHelper.WriteSoapMessage (xtw, methodInfo, SoapHeaderDirection.Fault, new Fault (message.Exception), message.Headers, soap12);
- }
- }
- else {
- #if NET_2_0
- if (soap12)
- WebServiceHelper.WriteSoapMessage (xtw, SoapBindingUse.Literal, Soap12Fault.Serializer, null, new Soap12Fault (message.Exception), null, soap12);
- else
- #endif
- {
- WebServiceHelper.WriteSoapMessage (xtw, SoapBindingUse.Literal, Fault.Serializer, null, new Fault (message.Exception), null, soap12);
- }
- }
- if (bufferResponse)
- {
- message.SetStage (SoapMessageStage.AfterSerialize);
- SoapExtension.ExecuteProcessMessage (_extensionChainLowPrio, message, outStream, true);
- SoapExtension.ExecuteProcessMessage (_extensionChainMedPrio, message, outStream, true);
- SoapExtension.ExecuteProcessMessage (_extensionChainHighPrio, message, outStream, true);
- }
-
- xtw.Flush ();
- }
- 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;
- }
- }
- 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);
- #if NET_2_0
- object soapVer = context.Items ["WebServiceSoapVersion"];
- if (soapVer != null)
- faultMessage.SetSoapVersion ((SoapProtocolVersion) soapVer);
- #endif
- SerializeResponse (context.Response, faultMessage);
- context.Response.End ();
- return;
- }
-
- private SoapServerMessage Invoke (HttpContext ctx, SoapServerMessage requestMessage)
- {
- 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;
- }
- }
- }
|