H
hugedifference
On day 3 of debugging this. I've implemented a simple soap extension
to intercept the servers outgoing soap message stream and replace it
with my own (code below).
Everything seems to work in the code step through except the server
side afterserialize stage is called twice (2 times!) but why? It
should only fire once afaik. In the second fire, afterserialize writes
an empty stream to the output stream, but I already wrote to the output
stream correctly on the first fire! My client says, expectedly, the
above error, the service returns nothing==empty stream.
I've found many hits on the net, but no answers. Day 4 is coming.
Argggh! The only post I found with an answer was this one:
http://www.thescripts.com/forum/thread375954.html but I don't think it
applies in my case as I rewound the stream correctly (or at least I
think I did).
I'm using vs 2005 and I'm running on XP pro. I'm using the embedded
webserver in 2005 (cassini?)
As I said, everything appears to work fine. On the server side It
takes the input message, transforms it, and then writes it to the
output stream. I checked the stream it copies to the output (in
afterserialize on the first time it fires) and everything looks
hunky-dory. However, the second time afterserialize fires everything
is messed up and the streams are empty (appoutputstream and
httpoutputstream).
all code below here
--*********************
Here is the soap extension
--**********************
using System;
using System.Web;
using System.Web.Services;
using System.IO;
using System.Web.Services.Protocols;
using System.Xml;
public class XmlStreamSoapExtension :
System.Web.Services.Protocols.SoapExtension
{
bool output = false;
Stream httpOutputStream;
Stream chainedOutputStream;
Stream appOutputStream;
public override Stream ChainStream(Stream stream)
{
Stream result = stream;
if ((output))
{
httpOutputStream = stream;
chainedOutputStream = new MemoryStream();
result = chainedOutputStream;
}
else
{
output = true;
}
return result;
}
public override object GetInitializer(System.Type serviceType)
{
return null;
}
public override object
GetInitializer(System.Web.Services.Protocols.LogicalMethodInfo
methodInfo, System.Web.Services.Protocols.SoapExtensionAttribute
attribute)
{
return null;
}
public override void Initialize(object initializer)
{
}
public override void ProcessMessage(SoapMessage message)
{
if (message.Stage == SoapMessageStage.AfterDeserialize)
{
HttpContext.Current.Request.InputStream.Position = 0;
HttpContext.Current.Items["SoapInputStream"] =
HttpContext.Current.Request.InputStream;
appOutputStream = new MemoryStream();
HttpContext.Current.Items["SoapOutputStream"] =
appOutputStream;
}
else if (message.Stage == SoapMessageStage.AfterSerialize)
{
chainedOutputStream.Position = 0;
XmlReader reader = new
XmlTextReader(chainedOutputStream);
reader.ReadStartElement("Envelope",
"http://schemas.xmlsoap.org/soap/envelope/");
reader.MoveToContent();
if ((reader.LocalName == "Header"))
{
reader.Skip();
}
reader.ReadStartElement("Body",
"http://schemas.xmlsoap.org/soap/envelope/");
reader.MoveToContent();
if ((reader.LocalName == "Fault" & reader.NamespaceURI
== "http://schemas.xmlsoap.org/soap/envelope/"))
{
chainedOutputStream.Position = 0;
CopyStream(chainedOutputStream, httpOutputStream);
}
else
{
appOutputStream.Flush();
appOutputStream.Position = 0;
CopyStream(appOutputStream, httpOutputStream);
}
appOutputStream.Close();
}
}
private void CopyStream(Stream src, Stream dest)
{
StreamReader reader = new StreamReader(src);
StreamWriter writer = new StreamWriter(dest);
writer.Write(reader.ReadToEnd());
writer.Flush();
}
public class SoapStreams
{
public static Stream InputMessage
{
get
{
return
((Stream)(HttpContext.Current.Items["SoapInputStream"]));
}
}
public static Stream OutputMessage
{
get
{
return
((Stream)(HttpContext.Current.Items["SoapOutputStream"]));
}
}
}
[AttributeUsage(AttributeTargets.Method)]
public class XmlStreamSoapExtensionAttribute :
SoapExtensionAttribute
{
private int m_priority;
public override Type ExtensionType
{
get{ return typeof(XmlStreamSoapExtension);}
}
public override int Priority
{
get {return m_priority;}
set{m_priority = 1;}
}
}
--*********************
end of soap extension class
--**********************
--*********************
--here is the asmx file --all it does is a simple transform of the
input message
--*********************
using System;
using System.Web;
using System.Collections;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Xml;
using System.Xml.Xsl;
using System.Xml.Serialization;
[System.Web.Services.WebServiceBindingAttribute(Name = "Arithmetic",
Namespace = "urn:msdn-microsoft-com:hows")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class arithmetic : WebService {
private const string soapURI =
"http://schemas.xmlsoap.org/soap/envelope/";
private const string nsURI = "urn:msdn-microsoft-com:hows";
[XmlStreamSoapExtension]
[WebMethod]
[SoapDocumentMethod("urn:msdn-microsoft-com:hows/Add", Use =
System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle
= System.Web.Services.Protocols.SoapParameterStyle.Bare)]
[return: XmlElement("AddResponse", Namespace =
"urn:msdn-microsoft-com:hows")]
public /* AddResponse */ void Add(/* Add Add1 */)
{
//************
//this will transform the input stream (in a reader) directly
to output stream
//************
XslCompiledTransform docXsl = new XslCompiledTransform();
XsltSettings xslset = new XsltSettings();
xslset.EnableDocumentFunction = true;
string xslsource = "add.xslt";
XmlUrlResolver resolver = new XmlUrlResolver();
string pathxsl = HttpContext.Current.Server.MapPath(xslsource);
docXsl.Load(new XmlTextReader(pathxsl), xslset, resolver);
docXsl.Transform(xReader, new
XmlTextWriter(SoapStreams.OutputMessage, System.Text.Encoding.UTF8));
}
public arithmetic () {
//Uncomment the following line if using designed components
//InitializeComponent();
}
}
--***************************
--end of the asmx file
--****************************
--**********************
--here is the xml message sent to the service by the client w/soap
action of
--"urn:msdn-microsoft-com:hows/Add"
--which works fine as it fires the service up
--************************
<?xml version='1.0' encoding='utf-8'?><soap:Envelope
xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns:xsd='http://www.w3.org/2001/XMLSchema'><soap:Body><Add
xmlns='urn:msdn-microsoft-com:hows'><n1>10</n1><n2>20</n2></Add></soap:Body></soap:Envelope>
--*****************
--end of the input message
--*****************
--*****************
--here is the add.xslt file
--****************
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xsl:version="1.0">
<soap:Body>
<ns:AddResponse xmlns:ns="urn:msdn-microsoft-com:hows">
<ns:sum>
<xsl:value-of select="sum(//ns:Add/*/text())"/>
</ns:sum>
</ns:AddResponse>
</soap:Body>
</soap:Envelope>
--******************
--end of add.xslt file
--******************
to intercept the servers outgoing soap message stream and replace it
with my own (code below).
Everything seems to work in the code step through except the server
side afterserialize stage is called twice (2 times!) but why? It
should only fire once afaik. In the second fire, afterserialize writes
an empty stream to the output stream, but I already wrote to the output
stream correctly on the first fire! My client says, expectedly, the
above error, the service returns nothing==empty stream.
I've found many hits on the net, but no answers. Day 4 is coming.
Argggh! The only post I found with an answer was this one:
http://www.thescripts.com/forum/thread375954.html but I don't think it
applies in my case as I rewound the stream correctly (or at least I
think I did).
I'm using vs 2005 and I'm running on XP pro. I'm using the embedded
webserver in 2005 (cassini?)
As I said, everything appears to work fine. On the server side It
takes the input message, transforms it, and then writes it to the
output stream. I checked the stream it copies to the output (in
afterserialize on the first time it fires) and everything looks
hunky-dory. However, the second time afterserialize fires everything
is messed up and the streams are empty (appoutputstream and
httpoutputstream).
all code below here
--*********************
Here is the soap extension
--**********************
using System;
using System.Web;
using System.Web.Services;
using System.IO;
using System.Web.Services.Protocols;
using System.Xml;
public class XmlStreamSoapExtension :
System.Web.Services.Protocols.SoapExtension
{
bool output = false;
Stream httpOutputStream;
Stream chainedOutputStream;
Stream appOutputStream;
public override Stream ChainStream(Stream stream)
{
Stream result = stream;
if ((output))
{
httpOutputStream = stream;
chainedOutputStream = new MemoryStream();
result = chainedOutputStream;
}
else
{
output = true;
}
return result;
}
public override object GetInitializer(System.Type serviceType)
{
return null;
}
public override object
GetInitializer(System.Web.Services.Protocols.LogicalMethodInfo
methodInfo, System.Web.Services.Protocols.SoapExtensionAttribute
attribute)
{
return null;
}
public override void Initialize(object initializer)
{
}
public override void ProcessMessage(SoapMessage message)
{
if (message.Stage == SoapMessageStage.AfterDeserialize)
{
HttpContext.Current.Request.InputStream.Position = 0;
HttpContext.Current.Items["SoapInputStream"] =
HttpContext.Current.Request.InputStream;
appOutputStream = new MemoryStream();
HttpContext.Current.Items["SoapOutputStream"] =
appOutputStream;
}
else if (message.Stage == SoapMessageStage.AfterSerialize)
{
chainedOutputStream.Position = 0;
XmlReader reader = new
XmlTextReader(chainedOutputStream);
reader.ReadStartElement("Envelope",
"http://schemas.xmlsoap.org/soap/envelope/");
reader.MoveToContent();
if ((reader.LocalName == "Header"))
{
reader.Skip();
}
reader.ReadStartElement("Body",
"http://schemas.xmlsoap.org/soap/envelope/");
reader.MoveToContent();
if ((reader.LocalName == "Fault" & reader.NamespaceURI
== "http://schemas.xmlsoap.org/soap/envelope/"))
{
chainedOutputStream.Position = 0;
CopyStream(chainedOutputStream, httpOutputStream);
}
else
{
appOutputStream.Flush();
appOutputStream.Position = 0;
CopyStream(appOutputStream, httpOutputStream);
}
appOutputStream.Close();
}
}
private void CopyStream(Stream src, Stream dest)
{
StreamReader reader = new StreamReader(src);
StreamWriter writer = new StreamWriter(dest);
writer.Write(reader.ReadToEnd());
writer.Flush();
}
public class SoapStreams
{
public static Stream InputMessage
{
get
{
return
((Stream)(HttpContext.Current.Items["SoapInputStream"]));
}
}
public static Stream OutputMessage
{
get
{
return
((Stream)(HttpContext.Current.Items["SoapOutputStream"]));
}
}
}
[AttributeUsage(AttributeTargets.Method)]
public class XmlStreamSoapExtensionAttribute :
SoapExtensionAttribute
{
private int m_priority;
public override Type ExtensionType
{
get{ return typeof(XmlStreamSoapExtension);}
}
public override int Priority
{
get {return m_priority;}
set{m_priority = 1;}
}
}
--*********************
end of soap extension class
--**********************
--*********************
--here is the asmx file --all it does is a simple transform of the
input message
--*********************
using System;
using System.Web;
using System.Collections;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Xml;
using System.Xml.Xsl;
using System.Xml.Serialization;
[System.Web.Services.WebServiceBindingAttribute(Name = "Arithmetic",
Namespace = "urn:msdn-microsoft-com:hows")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class arithmetic : WebService {
private const string soapURI =
"http://schemas.xmlsoap.org/soap/envelope/";
private const string nsURI = "urn:msdn-microsoft-com:hows";
[XmlStreamSoapExtension]
[WebMethod]
[SoapDocumentMethod("urn:msdn-microsoft-com:hows/Add", Use =
System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle
= System.Web.Services.Protocols.SoapParameterStyle.Bare)]
[return: XmlElement("AddResponse", Namespace =
"urn:msdn-microsoft-com:hows")]
public /* AddResponse */ void Add(/* Add Add1 */)
{
//************
//this will transform the input stream (in a reader) directly
to output stream
//************
XslCompiledTransform docXsl = new XslCompiledTransform();
XsltSettings xslset = new XsltSettings();
xslset.EnableDocumentFunction = true;
string xslsource = "add.xslt";
XmlUrlResolver resolver = new XmlUrlResolver();
string pathxsl = HttpContext.Current.Server.MapPath(xslsource);
docXsl.Load(new XmlTextReader(pathxsl), xslset, resolver);
docXsl.Transform(xReader, new
XmlTextWriter(SoapStreams.OutputMessage, System.Text.Encoding.UTF8));
}
public arithmetic () {
//Uncomment the following line if using designed components
//InitializeComponent();
}
}
--***************************
--end of the asmx file
--****************************
--**********************
--here is the xml message sent to the service by the client w/soap
action of
--"urn:msdn-microsoft-com:hows/Add"
--which works fine as it fires the service up
--************************
<?xml version='1.0' encoding='utf-8'?><soap:Envelope
xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns:xsd='http://www.w3.org/2001/XMLSchema'><soap:Body><Add
xmlns='urn:msdn-microsoft-com:hows'><n1>10</n1><n2>20</n2></Add></soap:Body></soap:Envelope>
--*****************
--end of the input message
--*****************
--*****************
--here is the add.xslt file
--****************
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xsl:version="1.0">
<soap:Body>
<ns:AddResponse xmlns:ns="urn:msdn-microsoft-com:hows">
<ns:sum>
<xsl:value-of select="sum(//ns:Add/*/text())"/>
</ns:sum>
</ns:AddResponse>
</soap:Body>
</soap:Envelope>
--******************
--end of add.xslt file
--******************