|
//------------------------------------------------------------------------------
// <copyright file="SoapHeader.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Web.Services.Protocols {
using System.Web.Services;
using System.Xml.Serialization;
using System;
using System.Reflection;
using System.Xml;
using System.Collections;
using System.IO;
using System.ComponentModel;
using System.Threading;
using System.Security.Permissions;
using System.Runtime.InteropServices;
using System.Web.Services.Diagnostics;
/// <include file='doc\SoapHeader.uex' path='docs/doc[@for="SoapHeader"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[XmlType(IncludeInSchema = false), SoapType(IncludeInSchema = false)]
public abstract class SoapHeader {
string actor;
bool mustUnderstand;
bool didUnderstand;
bool relay;
// prop getters should return a value when version == Default or when version == correctVersion.
// all version tests in getters should use != incorrectVersion
internal SoapProtocolVersion version = SoapProtocolVersion.Default;
/// <include file='doc\SoapHeader.uex' path='docs/doc[@for="SoapHeader.MustUnderstandEncoded"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[XmlAttribute("mustUnderstand", Namespace = "http://schemas.xmlsoap.org/soap/envelope/"),
SoapAttribute("mustUnderstand", Namespace = "http://schemas.xmlsoap.org/soap/envelope/"),
DefaultValue("0")]
public string EncodedMustUnderstand {
get { return version != SoapProtocolVersion.Soap12 && MustUnderstand ? "1" : "0"; }
set {
switch (value) {
case "false":
case "0": MustUnderstand = false; break;
case "true":
case "1": MustUnderstand = true; break;
default: throw new ArgumentException(Res.GetString(Res.WebHeaderInvalidMustUnderstand, value));
}
}
}
/// <include file='doc\SoapHeader.uex' path='docs/doc[@for="SoapHeader.EncodedMustUnderstand12"]/*' />
[XmlAttribute("mustUnderstand", Namespace = Soap12.Namespace),
SoapAttribute("mustUnderstand", Namespace = Soap12.Namespace),
DefaultValue("0"),
ComVisible(false)]
public string EncodedMustUnderstand12 {
get { return version != SoapProtocolVersion.Soap11 && MustUnderstand ? "1" : "0"; }
set {
EncodedMustUnderstand = value;
}
}
/// <include file='doc\SoapHeader.uex' path='docs/doc[@for="SoapHeader.MustUnderstand"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[XmlIgnore, SoapIgnore]
public bool MustUnderstand {
get { return InternalMustUnderstand; }
set { InternalMustUnderstand = value; }
}
internal virtual bool InternalMustUnderstand {
[PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust")]
get { return mustUnderstand; }
[PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust")]
set { mustUnderstand = value; }
}
/// <include file='doc\SoapHeader.uex' path='docs/doc[@for="SoapHeader.Actor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[XmlAttribute("actor", Namespace = "http://schemas.xmlsoap.org/soap/envelope/"),
SoapAttribute("actor", Namespace = "http://schemas.xmlsoap.org/soap/envelope/"),
DefaultValue("")]
public string Actor {
get { return version != SoapProtocolVersion.Soap12 ? InternalActor : ""; }
set { InternalActor = value; }
}
/// <include file='doc\SoapHeader.uex' path='docs/doc[@for="SoapHeader.Role"]/*' />
[XmlAttribute("role", Namespace = Soap12.Namespace),
SoapAttribute("role", Namespace = Soap12.Namespace),
DefaultValue(""),
ComVisible(false)]
public string Role {
get { return version != SoapProtocolVersion.Soap11 ? InternalActor : ""; }
set { InternalActor = value; }
}
internal virtual string InternalActor {
[PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust")]
get { return actor == null ? string.Empty : actor; }
[PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust")]
set { actor = value; }
}
/// <include file='doc\SoapHeader.uex' path='docs/doc[@for="SoapHeader.DidUnderstand"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[XmlIgnore, SoapIgnore]
public bool DidUnderstand {
get { return didUnderstand; }
set { didUnderstand = value; }
}
/// <include file='doc\SoapHeader.uex' path='docs/doc[@for="SoapHeader.EncodedRelay"]/*' />
[XmlAttribute("relay", Namespace = Soap12.Namespace),
SoapAttribute("relay", Namespace = Soap12.Namespace),
DefaultValue("0"),
ComVisible(false)]
public string EncodedRelay {
get { return version != SoapProtocolVersion.Soap11 && Relay ? "1" : "0"; }
set {
switch (value) {
case "false":
case "0": Relay = false; break;
case "true":
case "1": Relay = true; break;
default: throw new ArgumentException(Res.GetString(Res.WebHeaderInvalidRelay, value));
}
}
}
/// <include file='doc\SoapHeader.uex' path='docs/doc[@for="SoapHeader.Relay"]/*' />
[XmlIgnore, SoapIgnore, ComVisible(false)]
public bool Relay {
get { return InternalRelay; }
set { InternalRelay = value; }
}
internal virtual bool InternalRelay {
[PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust")]
get { return relay; }
[PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust")]
set { relay = value; }
}
}
[PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
public sealed class SoapHeaderMapping {
//
// Block external construction
//
internal SoapHeaderMapping() {
}
internal Type headerType;
internal bool repeats;
internal bool custom;
internal SoapHeaderDirection direction;
internal MemberInfo memberInfo;
public Type HeaderType {
get {
return headerType;
}
}
public bool Repeats {
get {
return repeats;
}
}
public bool Custom {
get {
return custom;
}
}
public SoapHeaderDirection Direction {
get {
return direction;
}
}
public MemberInfo MemberInfo {
get {
return memberInfo;
}
}
}
[PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
public sealed class SoapHeaderHandling {
SoapHeaderCollection unknownHeaders;
SoapHeaderCollection unreferencedHeaders;
int currentThread;
string envelopeNS;
void OnUnknownElement(object sender, XmlElementEventArgs e) {
if (Thread.CurrentThread.GetHashCode() != this.currentThread) return;
if (e.Element == null) return;
SoapUnknownHeader header = new SoapUnknownHeader();
header.Element = e.Element;
unknownHeaders.Add(header);
}
void OnUnreferencedObject(object sender, UnreferencedObjectEventArgs e) {
if (Thread.CurrentThread.GetHashCode() != this.currentThread) return;
object o = e.UnreferencedObject;
if (o == null) return;
if (typeof(SoapHeader).IsAssignableFrom(o.GetType())) {
unreferencedHeaders.Add((SoapHeader)o);
}
}
// return first missing header name;
public string ReadHeaders(XmlReader reader, XmlSerializer serializer, SoapHeaderCollection headers, SoapHeaderMapping[] mappings, SoapHeaderDirection direction, string envelopeNS, string encodingStyle, bool checkRequiredHeaders) {
string missingHeader = null;
reader.MoveToContent();
if (!reader.IsStartElement(Soap.Element.Header, envelopeNS)) {
if (checkRequiredHeaders && mappings != null && mappings.Length > 0)
missingHeader = GetHeaderElementName(mappings[0].headerType);
return missingHeader;
}
if (reader.IsEmptyElement) { reader.Skip(); return missingHeader; }
this.unknownHeaders = new SoapHeaderCollection();
this.unreferencedHeaders = new SoapHeaderCollection();
// thread hash code is used to differentiate between deserializations in event callbacks
this.currentThread = Thread.CurrentThread.GetHashCode();
this.envelopeNS = envelopeNS;
int depth = reader.Depth;
reader.ReadStartElement();
reader.MoveToContent();
XmlDeserializationEvents events = new XmlDeserializationEvents();
events.OnUnknownElement = new XmlElementEventHandler(this.OnUnknownElement);
events.OnUnreferencedObject = new UnreferencedObjectEventHandler(this.OnUnreferencedObject);
TraceMethod caller = Tracing.On ? new TraceMethod(this, "ReadHeaders") : null;
if (Tracing.On) Tracing.Enter(Tracing.TraceId(Res.TraceReadHeaders), caller, new TraceMethod(serializer, "Deserialize", reader, encodingStyle));
object[] headerValues = (object[])serializer.Deserialize(reader, encodingStyle, events);
if (Tracing.On) Tracing.Exit(Tracing.TraceId(Res.TraceReadHeaders), caller);
for (int i = 0; i < headerValues.Length; i++) {
if (headerValues[i] != null) {
SoapHeader header = (SoapHeader)headerValues[i];
header.DidUnderstand = true;
headers.Add(header);
}
else if (checkRequiredHeaders) {
// 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.
if (missingHeader == null)
missingHeader = GetHeaderElementName(mappings[i].headerType);
}
}
this.currentThread = 0;
this.envelopeNS = null;
foreach (SoapHeader header in this.unreferencedHeaders) {
headers.Add(header);
}
this.unreferencedHeaders = null;
foreach (SoapHeader header in this.unknownHeaders) {
headers.Add(header);
}
this.unknownHeaders = null;
// 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();
}
return missingHeader;
}
public static void WriteHeaders(XmlWriter writer, XmlSerializer serializer, SoapHeaderCollection headers, SoapHeaderMapping[] mappings, SoapHeaderDirection direction, bool isEncoded, string defaultNS, bool serviceDefaultIsEncoded, string envelopeNS) {
if (headers.Count == 0) return;
writer.WriteStartElement(Soap.Element.Header, envelopeNS);
SoapProtocolVersion version;
string encodingStyle;
if (envelopeNS == Soap12.Namespace) {
version = SoapProtocolVersion.Soap12;
encodingStyle = Soap12.Encoding;
}
else {
version = SoapProtocolVersion.Soap11;
encodingStyle = Soap.Encoding;
}
int unknownHeaderCount = 0;
ArrayList otherHeaders = new ArrayList();
SoapHeader[] headerArray = new SoapHeader[mappings.Length];
bool[] headerSet = new bool[headerArray.Length];
for (int i = 0; i < headers.Count; i++) {
SoapHeader header = headers[i];
if (header == null) continue;
int headerPosition;
header.version = version;
if (header is SoapUnknownHeader) {
otherHeaders.Add(header);
unknownHeaderCount++;
}
else if ((headerPosition = FindMapping(mappings, header, direction)) >= 0 && !headerSet[headerPosition]) {
headerArray[headerPosition] = header;
headerSet[headerPosition] = true;
}
else {
otherHeaders.Add(header);
}
}
int otherHeaderCount = otherHeaders.Count - unknownHeaderCount;
if (isEncoded && otherHeaderCount > 0) {
SoapHeader[] newHeaderArray = new SoapHeader[mappings.Length + otherHeaderCount];
headerArray.CopyTo(newHeaderArray, 0);
// fill in the non-statically known headers (otherHeaders) starting after the statically-known ones
int count = mappings.Length;
for (int i = 0; i < otherHeaders.Count; i++) {
if (!(otherHeaders[i] is SoapUnknownHeader))
newHeaderArray[count++] = (SoapHeader)otherHeaders[i];
}
headerArray = newHeaderArray;
}
TraceMethod caller = Tracing.On ? new TraceMethod(typeof(SoapHeaderHandling), "WriteHeaders") : null;
if (Tracing.On) Tracing.Enter(Tracing.TraceId(Res.TraceWriteHeaders), caller, new TraceMethod(serializer, "Serialize", writer, headerArray, null, isEncoded ? encodingStyle : null, "h_"));
serializer.Serialize(writer, headerArray, null, isEncoded ? encodingStyle : null, "h_");
if (Tracing.On) Tracing.Exit(Tracing.TraceId(Res.TraceWriteHeaders), caller);
foreach (SoapHeader header in otherHeaders) {
if (header is SoapUnknownHeader) {
SoapUnknownHeader unknown = (SoapUnknownHeader)header;
if (unknown.Element != null)
unknown.Element.WriteTo(writer);
}
else if (!isEncoded) { // encoded headers already appended to members mapping
string ns = SoapReflector.GetLiteralNamespace(defaultNS, serviceDefaultIsEncoded);
XmlSerializer headerSerializer = new XmlSerializer(header.GetType(), ns);
if (Tracing.On) Tracing.Enter(Tracing.TraceId(Res.TraceWriteHeaders), caller, new TraceMethod(headerSerializer, "Serialize", writer, header));
headerSerializer.Serialize(writer, header);
if (Tracing.On) Tracing.Exit(Tracing.TraceId(Res.TraceWriteHeaders), caller);
}
}
// reset the soap version
for (int i = 0; i < headers.Count; i++) {
SoapHeader header = headers[i];
if (header != null)
header.version = SoapProtocolVersion.Default;
}
writer.WriteEndElement();
writer.Flush();
}
public static void WriteUnknownHeaders(XmlWriter writer, SoapHeaderCollection headers, string envelopeNS) {
bool first = true;
foreach (SoapHeader header in headers) {
SoapUnknownHeader unknown = header as SoapUnknownHeader;
if (unknown != null) {
if (first) {
writer.WriteStartElement(Soap.Element.Header, envelopeNS);
first = false;
}
if (unknown.Element != null)
unknown.Element.WriteTo(writer);
}
}
if (!first)
writer.WriteEndElement(); // </soap:Header>
}
public static void SetHeaderMembers(SoapHeaderCollection headers, object target, SoapHeaderMapping[] mappings, SoapHeaderDirection direction, bool client) {
bool[] headerHandled = new bool[headers.Count];
if (mappings != null) {
for (int i = 0; i < mappings.Length; i++) {
SoapHeaderMapping mapping = mappings[i];
if ((mapping.direction & direction) == 0) continue;
if (mapping.repeats) {
ArrayList list = new ArrayList();
for (int j = 0; j < headers.Count; j++) {
SoapHeader header = headers[j];
if (headerHandled[j]) continue;
if (mapping.headerType.IsAssignableFrom(header.GetType())) {
list.Add(header);
headerHandled[j] = true;
}
}
MemberHelper.SetValue(mapping.memberInfo, target, list.ToArray(mapping.headerType));
}
else {
bool handled = false;
for (int j = 0; j < headers.Count; j++) {
SoapHeader header = headers[j];
if (headerHandled[j]) continue;
if (mapping.headerType.IsAssignableFrom(header.GetType())) {
if (handled) {
header.DidUnderstand = false;
continue;
}
handled = true;
MemberHelper.SetValue(mapping.memberInfo, target, header);
headerHandled[j] = true;
}
}
}
}
}
for (int i = 0; i < headerHandled.Length; i++) {
if (!headerHandled[i]) {
SoapHeader header = headers[i];
if (header.MustUnderstand && !header.DidUnderstand) {
throw new SoapHeaderException(Res.GetString(Res.WebCannotUnderstandHeader, GetHeaderElementName(header)),
new XmlQualifiedName(Soap.Code.MustUnderstand, Soap.Namespace));
}
}
}
}
public static void GetHeaderMembers(SoapHeaderCollection headers, object target, SoapHeaderMapping[] mappings, SoapHeaderDirection direction, bool client) {
if (mappings == null || mappings.Length == 0) return;
for (int i = 0; i < mappings.Length; i++) {
SoapHeaderMapping mapping = mappings[i];
if ((mapping.direction & direction) == 0) continue;
object value = MemberHelper.GetValue(mapping.memberInfo, target);
if (mapping.repeats) {
object[] values = (object[])value;
if (values == null) continue;
for (int j = 0; j < values.Length; j++) {
if (values[j] != null) headers.Add((SoapHeader)values[j]);
}
}
else {
if (value != null) headers.Add((SoapHeader)value);
}
}
}
public static void EnsureHeadersUnderstood(SoapHeaderCollection headers) {
for (int i = 0; i < headers.Count; i++) {
SoapHeader header = headers[i];
if (header.MustUnderstand && !header.DidUnderstand) {
throw new SoapHeaderException(Res.GetString(Res.WebCannotUnderstandHeader, GetHeaderElementName(header)),
new XmlQualifiedName(Soap.Code.MustUnderstand, Soap.Namespace));
}
}
}
static int FindMapping(SoapHeaderMapping[] mappings, SoapHeader header, SoapHeaderDirection direction) {
if (mappings == null || mappings.Length == 0) return -1;
Type headerType = header.GetType();
for (int i = 0; i < mappings.Length; i++) {
SoapHeaderMapping mapping = mappings[i];
if ((mapping.direction & direction) == 0) continue;
if (!mapping.custom) continue;
if (mapping.headerType.IsAssignableFrom(headerType)) {
return i;
}
}
return -1;
}
static string GetHeaderElementName(Type headerType) {
XmlReflectionImporter importer = SoapReflector.CreateXmlImporter(null, false);
XmlTypeMapping mapping = importer.ImportTypeMapping(headerType);
return mapping.XsdElementName;
}
static string GetHeaderElementName(SoapHeader header) {
if (header is SoapUnknownHeader) {
return ((SoapUnknownHeader)header).Element.LocalName;
}
else {
return GetHeaderElementName(header.GetType());
}
}
}
}
|