|
//------------------------------------------------------------------------------
// <copyright file="SoapServerProtocol.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Web.Services.Protocols {
using System;
using System.Diagnostics;
using System.Collections;
using System.IO;
using System.Reflection;
using System.Xml.Serialization;
using System.Xml;
using System.Xml.Schema;
using System.Web.Services.Description;
using System.Text;
using System.Net;
using System.Web.Services.Configuration;
using System.Threading;
using System.Security.Policy;
using System.Security.Permissions;
using System.Web.Services.Diagnostics;
[PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
public sealed class SoapServerType : ServerType {
Hashtable methods = new Hashtable();
Hashtable duplicateMethods = new Hashtable();
internal SoapReflectedExtension[] HighPriExtensions;
internal SoapReflectedExtension[] LowPriExtensions;
internal object[] HighPriExtensionInitializers;
internal object[] LowPriExtensionInitializers;
internal string serviceNamespace;
internal bool serviceDefaultIsEncoded;
internal bool routingOnSoapAction;
internal WebServiceProtocols protocolsSupported;
public string ServiceNamespace
{
get
{
return serviceNamespace;
}
}
public bool ServiceDefaultIsEncoded
{
get
{
return serviceDefaultIsEncoded;
}
}
public bool ServiceRoutingOnSoapAction
{
get
{
return routingOnSoapAction;
}
}
public SoapServerType(Type type, WebServiceProtocols protocolsSupported) : base(type) {
this.protocolsSupported = protocolsSupported;
bool soap11 = (protocolsSupported & WebServiceProtocols.HttpSoap) != 0;
bool soap12 = (protocolsSupported & WebServiceProtocols.HttpSoap12) != 0;
LogicalMethodInfo[] methodInfos = WebMethodReflector.GetMethods(type);
ArrayList mappings = new ArrayList();
WebServiceAttribute serviceAttribute = WebServiceReflector.GetAttribute(type);
object soapServiceAttribute = SoapReflector.GetSoapServiceAttribute(type);
routingOnSoapAction = SoapReflector.GetSoapServiceRoutingStyle(soapServiceAttribute) == SoapServiceRoutingStyle.SoapAction;
serviceNamespace = serviceAttribute.Namespace;
serviceDefaultIsEncoded = SoapReflector.ServiceDefaultIsEncoded(type);
SoapReflectionImporter soapImporter = SoapReflector.CreateSoapImporter(serviceNamespace, serviceDefaultIsEncoded);
XmlReflectionImporter xmlImporter = SoapReflector.CreateXmlImporter(serviceNamespace, serviceDefaultIsEncoded);
SoapReflector.IncludeTypes(methodInfos, soapImporter);
WebMethodReflector.IncludeTypes(methodInfos, xmlImporter);
SoapReflectedMethod[] soapMethods = new SoapReflectedMethod[methodInfos.Length];
SoapExtensionTypeElementCollection extensionTypes = WebServicesSection.Current.SoapExtensionTypes;
ArrayList highPri = new ArrayList();
ArrayList lowPri = new ArrayList();
for (int i = 0; i < extensionTypes.Count; i++) {
SoapExtensionTypeElement element = extensionTypes[i];
if (element == null)
continue;
SoapReflectedExtension extension = new SoapReflectedExtension(element.Type, null, element.Priority);
if (element.Group == PriorityGroup.High)
highPri.Add(extension);
else
lowPri.Add(extension);
}
HighPriExtensions = (SoapReflectedExtension[]) highPri.ToArray(typeof(SoapReflectedExtension));
LowPriExtensions = (SoapReflectedExtension[]) lowPri.ToArray(typeof(SoapReflectedExtension));
Array.Sort(HighPriExtensions);
Array.Sort(LowPriExtensions);
HighPriExtensionInitializers = SoapReflectedExtension.GetInitializers(type, HighPriExtensions);
LowPriExtensionInitializers = SoapReflectedExtension.GetInitializers(type, LowPriExtensions);
for (int i = 0; i < methodInfos.Length; i++) {
LogicalMethodInfo methodInfo = methodInfos[i];
SoapReflectedMethod soapMethod = SoapReflector.ReflectMethod(methodInfo, false, xmlImporter, soapImporter, serviceAttribute.Namespace);
mappings.Add(soapMethod.requestMappings);
if (soapMethod.responseMappings != null) mappings.Add(soapMethod.responseMappings);
mappings.Add(soapMethod.inHeaderMappings);
if (soapMethod.outHeaderMappings != null) mappings.Add(soapMethod.outHeaderMappings);
soapMethods[i] = soapMethod;
}
XmlMapping[] xmlMappings = (XmlMapping[])mappings.ToArray(typeof(XmlMapping));
TraceMethod caller = Tracing.On ? new TraceMethod(this, ".ctor", type, protocolsSupported) : null;
if (Tracing.On) Tracing.Enter(Tracing.TraceId(Res.TraceCreateSerializer), caller, new TraceMethod(typeof(XmlSerializer), "FromMappings", xmlMappings, this.Evidence));
XmlSerializer[] serializers = null;
if (AppDomain.CurrentDomain.IsHomogenous) {
serializers = XmlSerializer.FromMappings(xmlMappings);
}
else {
#pragma warning disable 618 // If we're in a non-homogenous domain, legacy CAS mode is enabled, so passing through evidence will not fail
serializers = XmlSerializer.FromMappings((xmlMappings), this.Evidence);
#pragma warning restore 618
}
if (Tracing.On) Tracing.Exit(Tracing.TraceId(Res.TraceCreateSerializer), caller);
int count = 0;
for (int i = 0; i < soapMethods.Length; i++) {
SoapServerMethod serverMethod = new SoapServerMethod();
SoapReflectedMethod soapMethod = soapMethods[i];
serverMethod.parameterSerializer = serializers[count++];
if (soapMethod.responseMappings != null) serverMethod.returnSerializer = serializers[count++];
serverMethod.inHeaderSerializer = serializers[count++];
if (soapMethod.outHeaderMappings != null) serverMethod.outHeaderSerializer = serializers[count++];
serverMethod.methodInfo = soapMethod.methodInfo;
serverMethod.action = soapMethod.action;
serverMethod.extensions = soapMethod.extensions;
serverMethod.extensionInitializers = SoapReflectedExtension.GetInitializers(serverMethod.methodInfo, soapMethod.extensions);
serverMethod.oneWay = soapMethod.oneWay;
serverMethod.rpc = soapMethod.rpc;
serverMethod.use = soapMethod.use;
serverMethod.paramStyle = soapMethod.paramStyle;
serverMethod.wsiClaims = soapMethod.binding == null ? WsiProfiles.None : soapMethod.binding.ConformsTo;
ArrayList inHeaders = new ArrayList();
ArrayList outHeaders = new ArrayList();
for (int j = 0; j < soapMethod.headers.Length; j++) {
SoapHeaderMapping mapping = new SoapHeaderMapping();
SoapReflectedHeader soapHeader = soapMethod.headers[j];
mapping.memberInfo = soapHeader.memberInfo;
mapping.repeats = soapHeader.repeats;
mapping.custom = soapHeader.custom;
mapping.direction = soapHeader.direction;
mapping.headerType = soapHeader.headerType;
if (mapping.direction == SoapHeaderDirection.In)
inHeaders.Add(mapping);
else if (mapping.direction == SoapHeaderDirection.Out)
outHeaders.Add(mapping);
else {
inHeaders.Add(mapping);
outHeaders.Add(mapping);
}
}
serverMethod.inHeaderMappings = (SoapHeaderMapping[])inHeaders.ToArray(typeof(SoapHeaderMapping));
if (serverMethod.outHeaderSerializer != null)
serverMethod.outHeaderMappings = (SoapHeaderMapping[])outHeaders.ToArray(typeof(SoapHeaderMapping));
// check feasibility of routing on request element for soap 1.1
if (soap11 && !routingOnSoapAction && soapMethod.requestElementName.IsEmpty)
throw new SoapException(Res.GetString(Res.TheMethodDoesNotHaveARequestElementEither1, serverMethod.methodInfo.Name), new XmlQualifiedName(Soap.Code.Client, Soap.Namespace));
// we can lookup methods by action or request element
if (methods[soapMethod.action] == null)
methods[soapMethod.action] = serverMethod;
else {
// duplicate soap actions not allowed in soap 1.1 if we're routing on soap action
if (soap11 && routingOnSoapAction) {
SoapServerMethod duplicateMethod = (SoapServerMethod)methods[soapMethod.action];
throw new SoapException(Res.GetString(Res.TheMethodsAndUseTheSameSoapActionWhenTheService3, serverMethod.methodInfo.Name, duplicateMethod.methodInfo.Name, soapMethod.action), new XmlQualifiedName(Soap.Code.Client, Soap.Namespace));
}
duplicateMethods[soapMethod.action] = serverMethod;
}
if (methods[soapMethod.requestElementName] == null)
methods[soapMethod.requestElementName] = serverMethod;
else {
// duplicate request elements not allowed in soap 1.1 if we're routing on request element
if (soap11 && !routingOnSoapAction) {
SoapServerMethod duplicateMethod = (SoapServerMethod)methods[soapMethod.requestElementName];
throw new SoapException(Res.GetString(Res.TheMethodsAndUseTheSameRequestElementXmlns4, serverMethod.methodInfo.Name, duplicateMethod.methodInfo.Name, soapMethod.requestElementName.Name, soapMethod.requestElementName.Namespace), new XmlQualifiedName(Soap.Code.Client, Soap.Namespace));
}
duplicateMethods[soapMethod.requestElementName] = serverMethod;
}
}
}
public SoapServerMethod GetMethod(object key) {
return (SoapServerMethod)methods[key];
}
public SoapServerMethod GetDuplicateMethod(object key) {
return (SoapServerMethod)duplicateMethods[key];
}
}
[PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust")]
[PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
public class SoapServerProtocolFactory : ServerProtocolFactory {
// POST requests without pathinfo (the "/Foo" in "foo.asmx/Foo")
// are treated as soap. if the server supports both versions we route requests
// with soapaction to 1.1 and other requests to 1.2
protected override ServerProtocol CreateIfRequestCompatible(HttpRequest request) {
if (request.PathInfo.Length > 0)
return null;
if (request.HttpMethod != "POST")
// MethodNotAllowed = 405,
return new UnsupportedRequestProtocol(405);
// at this point we know it's probably soap. we're still not sure of the version
// but we leave that to the SoapServerProtocol to figure out
return new SoapServerProtocol();
}
}
[PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust")]
[PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
public class SoapServerProtocol : ServerProtocol {
SoapServerType serverType;
SoapServerMethod serverMethod;
SoapServerMessage message;
bool isOneWay;
Exception onewayInitException;
SoapProtocolVersion version;
WebServiceProtocols protocolsSupported;
SoapServerProtocolHelper helper;
//
// Default constructor is provided since WebServicesConfiguration is inaccessible
//
protected internal SoapServerProtocol()
{
this.protocolsSupported = WebServicesSection.Current.EnabledProtocols;
}
/// <include file='doc\SoapServerProtocol.uex' path='docs/doc[@for="SoapServerProtocol.GetWriterForMessage"]/*' />
/// <devdoc>
/// <para>
/// Allows to intercept XmlWriter creation.
/// </para>
/// </devdoc>
[PermissionSet(SecurityAction.LinkDemand | SecurityAction.InheritanceDemand, Name = "FullTrust")]
protected virtual XmlWriter GetWriterForMessage(SoapServerMessage message, int bufferSize) {
if (bufferSize < 512)
bufferSize = 512;
return new XmlTextWriter(new StreamWriter(message.Stream, new UTF8Encoding(false), bufferSize));
/*
XmlWriterSettings ws = new XmlWriterSettings();
ws.Encoding = new UTF8Encoding(false);
ws.Indent = false;
ws.NewLineHandling = NewLineHandling.None;
return XmlWriter.Create(message.Stream, ws);
*/
}
/// <include file='doc\SoapServerProtocol.uex' path='docs/doc[@for="SoapServerProtocol.GetReaderForMessage"]/*' />
/// <devdoc>
/// <para>
/// Allows to intercept XmlReader creation.
/// </para>
/// </devdoc>
[PermissionSet(SecurityAction.LinkDemand | SecurityAction.InheritanceDemand, Name = "FullTrust")]
protected virtual XmlReader GetReaderForMessage(SoapServerMessage message, int bufferSize) {
Encoding enc = RequestResponseUtils.GetEncoding2(message.ContentType);
if (bufferSize < 512)
bufferSize = 512;
int readTimeout = WebServicesSection.Current.SoapEnvelopeProcessing.ReadTimeout;
Int64 timeout = readTimeout < 0 ? 0L : (Int64)readTimeout * 10000000;
Int64 nowTicks = DateTime.UtcNow.Ticks;
Int64 timeoutTicks = Int64.MaxValue - timeout <= nowTicks ? Int64.MaxValue : nowTicks + timeout;
XmlTextReader reader;
if (enc != null) {
if (timeoutTicks == Int64.MaxValue) {
reader = new XmlTextReader(new StreamReader(message.Stream, enc, true, bufferSize));
}
else {
reader = new SoapEnvelopeReader(new StreamReader(message.Stream, enc, true, bufferSize), timeoutTicks);
}
}
else {
if (timeoutTicks == Int64.MaxValue) {
reader = new XmlTextReader(message.Stream);
}
else {
reader = new SoapEnvelopeReader(message.Stream, timeoutTicks);
}
}
reader.DtdProcessing = DtdProcessing.Prohibit;
reader.Normalization = true;
reader.XmlResolver = null;
return reader;
}
internal override bool Initialize() {
// try to guess the request version so we can handle any exceptions that might come up
GuessVersion();
message = new SoapServerMessage(this);
onewayInitException = null;
if (null == (serverType = (SoapServerType)GetFromCache(typeof(SoapServerProtocol), Type))
&& null == (serverType = (SoapServerType)GetFromCache(typeof(SoapServerProtocol), Type, true)))
{
lock (InternalSyncObject)
{
if (null == (serverType = (SoapServerType)GetFromCache(typeof(SoapServerProtocol), Type))
&& null == (serverType = (SoapServerType)GetFromCache(typeof(SoapServerProtocol), Type, true)))
{
bool excludeSchemeHostPortFromCachingKey = this.IsCacheUnderPressure(typeof(SoapServerProtocol), Type);
serverType = new SoapServerType(Type, protocolsSupported);
AddToCache(typeof(SoapServerProtocol), Type, serverType, excludeSchemeHostPortFromCachingKey);
}
}
}
// We delay throwing any exceptions out of the extension until we determine if the method is one-way or not.
Exception extensionException = null;
try {
message.highPriConfigExtensions = SoapMessage.InitializeExtensions(serverType.HighPriExtensions, serverType.HighPriExtensionInitializers);
//
// Allow derived classes to modify the high priority extensions list.
//
message.highPriConfigExtensions = ModifyInitializedExtensions(PriorityGroup.High, message.highPriConfigExtensions);
// For one-way methods we rely on Request.InputStream guaranteeing that the entire request body has arrived
message.SetStream(Request.InputStream);
#if DEBUG
//Debug.Assert(message.Stream.CanSeek, "Web services SOAP handler assumes a seekable stream.");
// use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
if (!message.Stream.CanSeek) throw new InvalidOperationException("Non-Seekable stream " + message.Stream.GetType().FullName + " Web services SOAP handler assumes a seekable stream.");
#endif
message.InitExtensionStreamChain(message.highPriConfigExtensions);
message.SetStage(SoapMessageStage.BeforeDeserialize);
message.ContentType = Request.ContentType;
message.ContentEncoding = Request.Headers[ContentType.ContentEncoding];
message.RunExtensions(message.highPriConfigExtensions, false);
extensionException = message.Exception;
}
catch (Exception e) {
if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
throw;
}
if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, this, "Initialize", e);
extensionException = e;
}
// set this here since we might throw before we init the other extensions
message.allExtensions = message.highPriConfigExtensions;
// maybe the extensions that just ran changed some of the request data so we can make a better version guess
GuessVersion();
try {
this.serverMethod = RouteRequest(message);
// the RouteRequest impl should throw an exception if it can't route the request but just in case...
if (this.serverMethod == null)
throw new SoapException(Res.GetString(Res.UnableToHandleRequest0), new XmlQualifiedName(Soap.Code.Server, Soap.Namespace));
}
catch (Exception e) {
if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
throw;
}
if (helper.RequestNamespace != null)
SetHelper(SoapServerProtocolHelper.GetHelper(this, helper.RequestNamespace));
// version mismatches override other errors
CheckHelperVersion();
throw;
}
this.isOneWay = serverMethod.oneWay;
if (extensionException == null) {
try {
SoapReflectedExtension[] otherReflectedExtensions = (SoapReflectedExtension[]) CombineExtensionsHelper(serverMethod.extensions, serverType.LowPriExtensions, typeof(SoapReflectedExtension));
object[] otherInitializers = (object[]) CombineExtensionsHelper(serverMethod.extensionInitializers, serverType.LowPriExtensionInitializers, typeof(object));
message.otherExtensions = SoapMessage.InitializeExtensions(otherReflectedExtensions, otherInitializers);
//
// Allow derived classes to modify the other extensions list.
//
message.otherExtensions = ModifyInitializedExtensions(PriorityGroup.Low, message.otherExtensions);
message.allExtensions = (SoapExtension[]) CombineExtensionsHelper(message.highPriConfigExtensions, message.otherExtensions, typeof(SoapExtension));
}
catch (Exception e) {
if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
throw;
}
if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, this, "Initialize", e);
extensionException = e;
}
}
if (extensionException != null) {
if (isOneWay)
onewayInitException = extensionException;
else if (extensionException is SoapException)
throw extensionException;
else
throw SoapException.Create(Version, Res.GetString(Res.WebConfigExtensionError), new XmlQualifiedName(Soap.Code.Server, Soap.Namespace), extensionException);
}
return true;
}
/// <devdoc>
/// Allows derived classes to reorder or to replace intialized soap extensions prior to
/// the system calling ChainStream or executing them in any stage.
/// </devdoc>
protected virtual SoapExtension[] ModifyInitializedExtensions(PriorityGroup group, SoapExtension[] extensions)
{
return extensions;
}
/// <devdoc>
/// Determines which SoapMethod should be invoked for a particular
/// message.
/// </devdoc>
protected virtual SoapServerMethod RouteRequest(SoapServerMessage message)
{
return helper.RouteRequest();
}
private void GuessVersion() {
// make a best guess as to the version. we'll get more info when we ---- the envelope
if (IsSupported(WebServiceProtocols.AnyHttpSoap)) {
// both versions supported, we need to pick one
if (Request.Headers[Soap.Action] == null || ContentType.MatchesBase(Request.ContentType, ContentType.ApplicationSoap))
SetHelper(new Soap12ServerProtocolHelper(this));
else
SetHelper(new Soap11ServerProtocolHelper(this));
}
else if (IsSupported(WebServiceProtocols.HttpSoap)) {
SetHelper(new Soap11ServerProtocolHelper(this));
}
else if (IsSupported(WebServiceProtocols.HttpSoap12)) {
SetHelper(new Soap12ServerProtocolHelper(this));
}
}
internal bool IsSupported(WebServiceProtocols protocol) {
return ((protocolsSupported & protocol) == protocol);
}
internal override ServerType ServerType {
get { return serverType; }
}
internal override LogicalMethodInfo MethodInfo {
get { return serverMethod.methodInfo; }
}
internal SoapServerMethod ServerMethod {
get { return serverMethod; }
}
internal SoapServerMessage Message {
get { return message; }
}
internal override bool IsOneWay {
get { return this.isOneWay; }
}
internal override Exception OnewayInitException {
get {
Debug.Assert(isOneWay || (onewayInitException == null), "initException is meant to be used for oneWay methods only.");
return this.onewayInitException;
}
}
internal SoapProtocolVersion Version {
get { return version; }
}
/*
internal bool IsInitialized {
get { return serverMethod != null; }
}
*/
internal override void CreateServerInstance() {
base.CreateServerInstance();
message.SetStage(SoapMessageStage.AfterDeserialize);
message.RunExtensions(message.allExtensions, true);
SoapHeaderHandling.SetHeaderMembers(message.Headers, this.Target, serverMethod.inHeaderMappings, SoapHeaderDirection.In, false);
}
/*
#if DEBUG
private static void CopyStream(Stream source, Stream dest) {
byte[] bytes = new byte[1024];
int numRead = 0;
while ((numRead = source.Read(bytes, 0, 1024)) > 0)
dest.Write(bytes, 0, numRead);
}
#endif
*/
private void SetHelper(SoapServerProtocolHelper helper) {
this.helper = helper;
this.version = helper.Version;
Context.Items[WebService.SoapVersionContextSlot] = helper.Version;
}
private static Array CombineExtensionsHelper(Array array1, Array array2, Type elementType) {
if (array1 == null) return array2;
if (array2 == null) return array1;
int length = array1.Length + array2.Length;
if (length == 0)
return null;
Array result = null;
if (elementType == typeof(SoapReflectedExtension))
result = new SoapReflectedExtension[length];
else if (elementType == typeof(SoapExtension))
result = new SoapExtension[length];
else if (elementType == typeof(object))
result = new object[length];
else
throw new ArgumentException(Res.GetString(Res.ElementTypeMustBeObjectOrSoapExtensionOrSoapReflectedException), "elementType");
Array.Copy(array1, 0, result, 0, array1.Length);
Array.Copy(array2, 0, result, array1.Length, array2.Length);
return result;
}
private void CheckHelperVersion() {
if (helper.RequestNamespace == null) return;
// looks at the helper request namespace and version information to see if we need to return a
// version mismatch fault (and if so, what version fault). there are two conditions to check:
// unknown envelope ns and known but unsupported envelope ns. there are a few rules this code must follow:
// * a 1.1 node responds with a 1.1 fault.
// * a 1.2 node responds to a 1.1 request with a 1.1 fault but responds to an unknown request with a 1.2 fault.
// * a both node can respond with either but we prefer 1.1.
// GetHelper returns an arbitrary helper when the envelope ns is unknown, so we can check the helper's
// expected envelope against the actual request ns to see if the request ns is unknown
if (helper.RequestNamespace != helper.EnvelopeNs) { // unknown envelope ns -- version mismatch
// respond with the version we support or 1.1 if we support both
string requestNamespace = helper.RequestNamespace;
if (IsSupported(WebServiceProtocols.HttpSoap))
SetHelper(new Soap11ServerProtocolHelper(this));
else
SetHelper(new Soap12ServerProtocolHelper(this));
throw new SoapException(Res.GetString(Res.WebInvalidEnvelopeNamespace, requestNamespace, helper.EnvelopeNs), SoapException.VersionMismatchFaultCode);
}
else if (!IsSupported(helper.Protocol)) { // known envelope ns but we don't support this version -- version mismatch
// always respond with 1.1
string requestNamespace = helper.RequestNamespace;
string expectedNamespace = IsSupported(WebServiceProtocols.HttpSoap) ? Soap.Namespace : Soap12.Namespace;
SetHelper(new Soap11ServerProtocolHelper(this));
throw new SoapException(Res.GetString(Res.WebInvalidEnvelopeNamespace, requestNamespace, expectedNamespace), SoapException.VersionMismatchFaultCode);
}
}
internal override object[] ReadParameters() {
message.InitExtensionStreamChain(message.otherExtensions);
message.RunExtensions(message.otherExtensions, true);
// do a sanity check on the content-type before we check the version since otherwise the error might be really nasty
if (!ContentType.IsSoap(message.ContentType))
throw new SoapException(Res.GetString(Res.WebRequestContent, message.ContentType, helper.HttpContentType),
new XmlQualifiedName(Soap.Code.Client, Soap.Namespace), new SoapFaultSubCode(Soap12FaultCodes.UnsupportedMediaTypeFaultCode));
// now that all the extensions have run, establish the real version of the request
XmlReader reader = null;
try {
reader = GetXmlReader();
reader.MoveToContent();
SetHelper(SoapServerProtocolHelper.GetHelper(this, reader.NamespaceURI));
}
catch (XmlException e) {
throw new SoapException(Res.GetString(Res.WebRequestUnableToRead), new XmlQualifiedName(Soap.Code.Client, Soap.Namespace), e);
}
CheckHelperVersion();
// now do a more specific content-type check for soap 1.1 only (soap 1.2 allows various xml content types)
if (version == SoapProtocolVersion.Soap11 && !ContentType.MatchesBase(message.ContentType, helper.HttpContentType))
throw new SoapException(Res.GetString(Res.WebRequestContent, message.ContentType, helper.HttpContentType),
new XmlQualifiedName(Soap.Code.Client, Soap.Namespace), new SoapFaultSubCode(Soap12FaultCodes.UnsupportedMediaTypeFaultCode));
if (message.Exception != null) {
throw message.Exception;
}
try {
if (!reader.IsStartElement(Soap.Element.Envelope, helper.EnvelopeNs))
throw new InvalidOperationException(Res.GetString(Res.WebMissingEnvelopeElement));
if (reader.IsEmptyElement)
throw new InvalidOperationException(Res.GetString(Res.WebMissingBodyElement));
int depth = reader.Depth;
reader.ReadStartElement(Soap.Element.Envelope, helper.EnvelopeNs);
reader.MoveToContent();
// run time check for R2738 A MESSAGE MUST include all soapbind:headers specified on a wsdl:input or wsdl:output of a wsdl:operationwsdl:binding that describes it.
bool checkRequiredHeaders = (this.serverMethod.wsiClaims & WsiProfiles.BasicProfile1_1) != 0 && version != SoapProtocolVersion.Soap12;
string missingHeader = new SoapHeaderHandling().ReadHeaders(reader, serverMethod.inHeaderSerializer, message.Headers, serverMethod.inHeaderMappings, SoapHeaderDirection.In, helper.EnvelopeNs, serverMethod.use == SoapBindingUse.Encoded ? helper.EncodingNs : null, checkRequiredHeaders);
if (missingHeader != null) {
throw new SoapHeaderException(Res.GetString(Res.WebMissingHeader, missingHeader),
new XmlQualifiedName(Soap.Code.MustUnderstand, Soap.Namespace));
}
if (!reader.IsStartElement(Soap.Element.Body, helper.EnvelopeNs))
throw new InvalidOperationException(Res.GetString(Res.WebMissingBodyElement));
reader.ReadStartElement(Soap.Element.Body, helper.EnvelopeNs);
reader.MoveToContent();
object[] values;
bool isEncodedSoap = serverMethod.use == SoapBindingUse.Encoded;
TraceMethod caller = Tracing.On ? new TraceMethod(this, "ReadParameters") : null;
if (Tracing.On) Tracing.Enter(Tracing.TraceId(Res.TraceReadRequest), caller, new TraceMethod(serverMethod.parameterSerializer, "Deserialize", reader, serverMethod.use == SoapBindingUse.Encoded ? helper.EncodingNs : null));
bool useDeserializationEvents = !isEncodedSoap && (WebServicesSection.Current.SoapEnvelopeProcessing.IsStrict || Tracing.On);
if (useDeserializationEvents) {
XmlDeserializationEvents events = Tracing.On ? Tracing.GetDeserializationEvents() : RuntimeUtils.GetDeserializationEvents();
values = (object[])serverMethod.parameterSerializer.Deserialize(reader, null, events);
}
else {
values = (object[])serverMethod.parameterSerializer.Deserialize(reader, isEncodedSoap ? helper.EncodingNs : null);
}
if (Tracing.On) Tracing.Exit(Tracing.TraceId(Res.TraceReadRequest), caller);
// Consume soap:Body and soap:Envelope closing tags
while (depth < reader.Depth && reader.Read()) {
// Nothing, just read on
}
// consume end tag
if (reader.NodeType == XmlNodeType.EndElement) {
reader.Read();
}
message.SetParameterValues(values);
return values;
}
catch (SoapException) {
throw;
}
catch (Exception e) {
if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
throw;
}
throw new SoapException(Res.GetString(Res.WebRequestUnableToRead), new XmlQualifiedName(Soap.Code.Client, Soap.Namespace), e);
}
}
internal override void WriteReturns(object[] returnValues, Stream outputStream) {
if (serverMethod.oneWay) return;
bool isEncoded = serverMethod.use == SoapBindingUse.Encoded;
SoapHeaderHandling.EnsureHeadersUnderstood(message.Headers);
message.Headers.Clear();
SoapHeaderHandling.GetHeaderMembers(message.Headers, this.Target, serverMethod.outHeaderMappings, SoapHeaderDirection.Out, false);
if (message.allExtensions != null)
message.SetExtensionStream(new SoapExtensionStream());
message.InitExtensionStreamChain(message.allExtensions);
message.SetStage(SoapMessageStage.BeforeSerialize);
message.ContentType = ContentType.Compose(helper.HttpContentType, Encoding.UTF8);
message.SetParameterValues(returnValues);
message.RunExtensions(message.allExtensions, true);
message.SetStream(outputStream);
Response.ContentType = message.ContentType;
if (message.ContentEncoding != null && message.ContentEncoding.Length > 0)
Response.AppendHeader(ContentType.ContentEncoding, message.ContentEncoding);
XmlWriter writer = GetWriterForMessage(message, 1024);
if (writer == null)
throw new InvalidOperationException(Res.GetString(Res.WebNullWriterForMessage));
writer.WriteStartDocument();
writer.WriteStartElement("soap", Soap.Element.Envelope, helper.EnvelopeNs);
writer.WriteAttributeString("xmlns", "soap", null, helper.EnvelopeNs);
if (isEncoded) {
writer.WriteAttributeString("xmlns", "soapenc", null, helper.EncodingNs);
writer.WriteAttributeString("xmlns", "tns", null, serverType.serviceNamespace);
writer.WriteAttributeString("xmlns", "types", null, SoapReflector.GetEncodedNamespace(serverType.serviceNamespace, serverType.serviceDefaultIsEncoded));
}
if (serverMethod.rpc && version == SoapProtocolVersion.Soap12) {
writer.WriteAttributeString("xmlns", "rpc", null, Soap12.RpcNamespace);
}
writer.WriteAttributeString("xmlns", "xsi", null, XmlSchema.InstanceNamespace);
writer.WriteAttributeString("xmlns", "xsd", null, XmlSchema.Namespace);
SoapHeaderHandling.WriteHeaders(writer, serverMethod.outHeaderSerializer, message.Headers, serverMethod.outHeaderMappings, SoapHeaderDirection.Out, isEncoded, serverType.serviceNamespace, serverType.serviceDefaultIsEncoded, helper.EnvelopeNs);
writer.WriteStartElement(Soap.Element.Body, helper.EnvelopeNs);
if (isEncoded && version != SoapProtocolVersion.Soap12) // don't write encodingStyle on soap:Body for soap 1.2
writer.WriteAttributeString("soap", Soap.Attribute.EncodingStyle, null, helper.EncodingNs);
TraceMethod caller = Tracing.On ? new TraceMethod(this, "WriteReturns") : null;
if (Tracing.On) Tracing.Enter(Tracing.TraceId(Res.TraceWriteResponse), caller, new TraceMethod(serverMethod.returnSerializer, "Serialize", writer, returnValues, null, isEncoded ? helper.EncodingNs : null));
serverMethod.returnSerializer.Serialize(writer, returnValues, null, isEncoded ? helper.EncodingNs : null);
if (Tracing.On) Tracing.Exit(Tracing.TraceId(Res.TraceWriteResponse), caller);
writer.WriteEndElement();
writer.WriteEndElement();
writer.Flush();
message.SetStage(SoapMessageStage.AfterSerialize);
message.RunExtensions(message.allExtensions, true);
}
internal override bool WriteException(Exception e, Stream outputStream) {
if (message == null) return false;
message.Headers.Clear();
if (serverMethod != null && this.Target != null)
SoapHeaderHandling.GetHeaderMembers(message.Headers, this.Target, serverMethod.outHeaderMappings, SoapHeaderDirection.Fault, false);
SoapException soapException;
if (e is SoapException)
soapException = (SoapException)e;
else if (serverMethod != null && serverMethod.rpc && helper.Version == SoapProtocolVersion.Soap12 && e is ArgumentException)
// special case to handle soap 1.2 rpc "BadArguments" fault
soapException = SoapException.Create(Version, Res.GetString(Res.WebRequestUnableToProcess), new XmlQualifiedName(Soap.Code.Client, Soap.Namespace), null, null, null, new SoapFaultSubCode(Soap12FaultCodes.RpcBadArgumentsFaultCode), e);
else
soapException = SoapException.Create(Version, Res.GetString(Res.WebRequestUnableToProcess), new XmlQualifiedName(Soap.Code.Server, Soap.Namespace), e);
if (SoapException.IsVersionMismatchFaultCode(soapException.Code)) {
if (IsSupported(WebServiceProtocols.HttpSoap12)) {
SoapUnknownHeader unknownHeader = CreateUpgradeHeader();
if (unknownHeader != null)
Message.Headers.Add(unknownHeader);
}
}
Response.ClearHeaders();
Response.Clear();
HttpStatusCode statusCode = helper.SetResponseErrorCode(Response, soapException);
bool disableExtensions = false;
SoapExtensionStream extensionStream = new SoapExtensionStream();
if (message.allExtensions != null)
message.SetExtensionStream(extensionStream);
try {
message.InitExtensionStreamChain(message.allExtensions);
}
catch (Exception ex) {
if (ex is ThreadAbortException || ex is StackOverflowException || ex is OutOfMemoryException) {
throw;
}
if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, this, "WriteException", ex);
disableExtensions = true;
}
message.SetStage(SoapMessageStage.BeforeSerialize);
message.ContentType = ContentType.Compose(helper.HttpContentType, Encoding.UTF8);
message.Exception = soapException;
if (!disableExtensions) {
try {
message.RunExtensions(message.allExtensions, false);
}
catch (Exception ex) {
if (ex is ThreadAbortException || ex is StackOverflowException || ex is OutOfMemoryException) {
throw;
}
if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, this, "WriteException", ex);
disableExtensions = true;
}
}
message.SetStream(outputStream);
Response.ContentType = message.ContentType;
if (message.ContentEncoding != null && message.ContentEncoding.Length > 0) {
Response.AppendHeader(ContentType.ContentEncoding, message.ContentEncoding);
}
XmlWriter writer = GetWriterForMessage(message, 512);
if (writer == null)
throw new InvalidOperationException(Res.GetString(Res.WebNullWriterForMessage));
helper.WriteFault(writer, message.Exception, statusCode);
if (!disableExtensions) {
SoapException extensionException = null;
try {
message.SetStage(SoapMessageStage.AfterSerialize);
message.RunExtensions(message.allExtensions, false);
}
catch (Exception ex)
{
if (ex is ThreadAbortException || ex is StackOverflowException || ex is OutOfMemoryException)
{
throw;
}
if (Tracing.On) Tracing.ExceptionCatch(TraceEventType.Warning, this, "WriteException", ex);
if (!extensionStream.HasWritten) {
// if we haven't already written to the stream, we may be able to send an error
extensionException = SoapException.Create(Version, Res.GetString(Res.WebExtensionError), new XmlQualifiedName(Soap.Code.Server, Soap.Namespace), ex);
}
}
if (extensionException != null) {
Response.ContentType = ContentType.Compose(ContentType.TextPlain, Encoding.UTF8);
StreamWriter sw = new StreamWriter(outputStream, new UTF8Encoding(false));
sw.WriteLine(GenerateFaultString(message.Exception));
sw.Flush();
}
}
return true;
}
bool WriteException_TryWriteFault(SoapServerMessage message, Stream outputStream, HttpStatusCode statusCode, bool disableExtensions)
{
return true;
}
internal SoapUnknownHeader CreateUpgradeHeader() {
XmlDocument doc = new XmlDocument();
XmlElement upgradeElement = doc.CreateElement(Soap12.Prefix, Soap12.Element.Upgrade, Soap12.Namespace);
if (IsSupported(WebServiceProtocols.HttpSoap))
upgradeElement.AppendChild(CreateUpgradeEnvelope(doc, Soap.Prefix, Soap.Namespace));
if (IsSupported(WebServiceProtocols.HttpSoap12))
upgradeElement.AppendChild(CreateUpgradeEnvelope(doc, Soap12.Prefix, Soap12.Namespace));
SoapUnknownHeader upgradeHeader = new SoapUnknownHeader();
upgradeHeader.Element = upgradeElement;
return upgradeHeader;
}
private static XmlElement CreateUpgradeEnvelope(XmlDocument doc, string prefix, string envelopeNs) {
XmlElement envelopeElement = doc.CreateElement(Soap12.Prefix, Soap12.Element.UpgradeEnvelope, Soap12.Namespace);
XmlAttribute xmlnsAttr = doc.CreateAttribute("xmlns", prefix, "http://www.w3.org/2000/xmlns/");
xmlnsAttr.Value = envelopeNs;
XmlAttribute qnameAttr = doc.CreateAttribute(Soap12.Attribute.UpgradeEnvelopeQname);
qnameAttr.Value = prefix + ":" + Soap.Element.Envelope;
envelopeElement.Attributes.Append(qnameAttr);
envelopeElement.Attributes.Append(xmlnsAttr);
return envelopeElement;
}
internal XmlReader GetXmlReader() {
Encoding enc = RequestResponseUtils.GetEncoding2(Message.ContentType);
// in the case of conformant service check that input encodig is utg8 or urf16
//
bool checkEncoding = serverMethod != null && (serverMethod.wsiClaims & WsiProfiles.BasicProfile1_1) != 0 && Version != SoapProtocolVersion.Soap12;
if (checkEncoding && enc != null) {
// WS-I BP 1.1: R1012: A MESSAGE MUST be serialized as either UTF-8 or UTF-16.
if (!(enc is UTF8Encoding) && !(enc is UnicodeEncoding)) {
throw new InvalidOperationException(Res.GetString(Res.WebWsiContentTypeEncoding));
}
}
XmlReader reader = GetReaderForMessage(Message, RequestResponseUtils.GetBufferSize(Request.ContentLength));
if (reader == null)
throw new InvalidOperationException(Res.GetString(Res.WebNullReaderForMessage));
return reader;
}
internal class SoapEnvelopeReader : XmlTextReader {
Int64 readerTimedout;
internal SoapEnvelopeReader(TextReader input, Int64 timeout) : base(input) {
this.readerTimedout = timeout;
}
internal SoapEnvelopeReader(Stream input, Int64 timeout) : base(input) {
this.readerTimedout = timeout;
}
public override bool Read() {
CheckTimeout();
return base.Read();
}
public override bool MoveToNextAttribute() {
CheckTimeout();
return base.MoveToNextAttribute();
}
public override XmlNodeType MoveToContent() {
CheckTimeout();
return base.MoveToContent();
}
private void CheckTimeout() {
if (DateTime.UtcNow.Ticks > readerTimedout) {
throw new InvalidOperationException(Res.GetString(Res.WebTimeout));
}
}
}
}
internal abstract class SoapServerProtocolHelper {
SoapServerProtocol protocol;
string requestNamespace;
protected SoapServerProtocolHelper(SoapServerProtocol protocol) {
this.protocol = protocol;
}
protected SoapServerProtocolHelper(SoapServerProtocol protocol, string requestNamespace) {
this.protocol = protocol;
this.requestNamespace = requestNamespace;
}
internal static SoapServerProtocolHelper GetHelper(SoapServerProtocol protocol, string envelopeNs) {
SoapServerProtocolHelper helper;
if (envelopeNs == Soap.Namespace)
helper = new Soap11ServerProtocolHelper(protocol, envelopeNs);
else if (envelopeNs == Soap12.Namespace)
helper = new Soap12ServerProtocolHelper(protocol, envelopeNs);
else
// just return a soap 1.1 helper -- the fact that the requestNs doesn't match will signal a version mismatch
helper = new Soap11ServerProtocolHelper(protocol, envelopeNs);
return helper;
}
internal HttpStatusCode SetResponseErrorCode(HttpResponse response, SoapException soapException) {
if (soapException.SubCode != null && soapException.SubCode.Code == Soap12FaultCodes.UnsupportedMediaTypeFaultCode) {
response.StatusCode = (int) HttpStatusCode.UnsupportedMediaType;
soapException.ClearSubCode();
}
else if (SoapException.IsClientFaultCode(soapException.Code)) {
System.Web.Services.Protocols.ServerProtocol.SetHttpResponseStatusCode(response,
(int)HttpStatusCode.InternalServerError);
for (Exception inner = soapException; inner != null; inner = inner.InnerException) {
if (inner is XmlException) {
response.StatusCode = (int) HttpStatusCode.BadRequest;
}
}
}
else {
System.Web.Services.Protocols.ServerProtocol.SetHttpResponseStatusCode(response,
(int)HttpStatusCode.InternalServerError);
}
response.StatusDescription = HttpWorkerRequest.GetStatusDescription(response.StatusCode);
return (HttpStatusCode)response.StatusCode;
}
internal abstract void WriteFault(XmlWriter writer, SoapException soapException, HttpStatusCode statusCode);
internal abstract SoapServerMethod RouteRequest();
internal abstract SoapProtocolVersion Version { get; }
internal abstract WebServiceProtocols Protocol { get; }
internal abstract string EnvelopeNs { get; }
internal abstract string EncodingNs { get; }
internal abstract string HttpContentType { get; }
internal string RequestNamespace {
get { return requestNamespace; }
}
protected SoapServerProtocol ServerProtocol {
get { return protocol; }
}
protected SoapServerType ServerType {
get { return (SoapServerType)protocol.ServerType; }
}
// tries to get to the first child element of body, ignoring details
// such as the namespace of Envelope and Body (a version mismatch check will come later)
protected XmlQualifiedName GetRequestElement() {
SoapServerMessage message = ServerProtocol.Message;
long savedPosition = message.Stream.Position;
XmlReader reader = protocol.GetXmlReader();
reader.MoveToContent();
requestNamespace = reader.NamespaceURI;
if (!reader.IsStartElement(Soap.Element.Envelope, requestNamespace))
throw new InvalidOperationException(Res.GetString(Res.WebMissingEnvelopeElement));
if (reader.IsEmptyElement)
throw new InvalidOperationException(Res.GetString(Res.WebMissingBodyElement));
reader.ReadStartElement(Soap.Element.Envelope, requestNamespace);
reader.MoveToContent();
while (!reader.EOF && !reader.IsStartElement(Soap.Element.Body, requestNamespace))
reader.Skip();
if (reader.EOF) {
throw new InvalidOperationException(Res.GetString(Res.WebMissingBodyElement));
}
XmlQualifiedName element;
if (reader.IsEmptyElement) {
element = XmlQualifiedName.Empty;
}
else {
reader.ReadStartElement(Soap.Element.Body, requestNamespace);
reader.MoveToContent();
element = new XmlQualifiedName(reader.LocalName, reader.NamespaceURI);
}
message.Stream.Position = savedPosition;
return element;
}
}
}
|