a575963da9
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
345 lines
9.2 KiB
C#
345 lines
9.2 KiB
C#
//
|
|
// MessageImpl.cs
|
|
//
|
|
// Author:
|
|
// Atsushi Enomoto <atsushi@ximian.com>
|
|
//
|
|
// Copyright (C) 2006 Novell, Inc. http://www.novell.com
|
|
//
|
|
// 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.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Runtime.Serialization;
|
|
using System.Xml;
|
|
using System.IO;
|
|
|
|
namespace System.ServiceModel.Channels
|
|
{
|
|
internal class AttributeInfo
|
|
{
|
|
public string Prefix, Name, Namespace, Value;
|
|
}
|
|
|
|
internal class AttributeCollection : List<AttributeInfo>
|
|
{
|
|
public AttributeCollection ()
|
|
{
|
|
}
|
|
|
|
public AttributeCollection (AttributeCollection copy)
|
|
: base (copy)
|
|
{
|
|
}
|
|
}
|
|
|
|
internal class XmlReaderMessage : Message
|
|
{
|
|
MessageVersion version;
|
|
XmlDictionaryReader reader;
|
|
MessageHeaders headers;
|
|
MessageProperties properties = new MessageProperties ();
|
|
bool is_empty, is_fault, body_started, body_consumed;
|
|
int max_headers;
|
|
AttributeCollection attributes;
|
|
|
|
public XmlReaderMessage (MessageVersion version, XmlDictionaryReader reader, int maxSizeOfHeaders)
|
|
{
|
|
this.version = version;
|
|
this.reader = reader;
|
|
this.max_headers = maxSizeOfHeaders;
|
|
|
|
ReadEnvelopeStart ();
|
|
}
|
|
|
|
public override MessageHeaders Headers {
|
|
get {
|
|
if (headers == null)
|
|
ReadHeaders ();
|
|
return headers;
|
|
}
|
|
}
|
|
|
|
public override bool IsEmpty {
|
|
get {
|
|
ReadBodyStart ();
|
|
return is_empty;
|
|
}
|
|
}
|
|
|
|
public override bool IsFault {
|
|
get {
|
|
ReadBodyStart ();
|
|
return is_fault;
|
|
}
|
|
}
|
|
|
|
public override MessageProperties Properties {
|
|
get { return properties; }
|
|
}
|
|
|
|
public override MessageVersion Version {
|
|
get { return version; }
|
|
}
|
|
|
|
protected override MessageBuffer OnCreateBufferedCopy (
|
|
int maxBufferSize)
|
|
{
|
|
ReadBodyStart ();
|
|
var headers = new MessageHeaders (Headers);
|
|
var props = new MessageProperties (Properties);
|
|
if (IsEmpty)
|
|
return new DefaultMessageBuffer (headers, props, attributes);
|
|
else
|
|
return new DefaultMessageBuffer (maxBufferSize, headers, props, new XmlReaderBodyWriter (reader), IsFault, attributes);
|
|
}
|
|
|
|
protected override string OnGetBodyAttribute (
|
|
string localName, string ns)
|
|
{
|
|
ReadBodyStart ();
|
|
var att = attributes.FirstOrDefault (a => a.Name == localName && a.Namespace == ns);
|
|
return att != null ? att.Value : null;
|
|
}
|
|
|
|
protected override XmlDictionaryReader OnGetReaderAtBodyContents ()
|
|
{
|
|
if (reader.ReadState == ReadState.Closed)
|
|
return reader; // silly, but that's what our test expects.
|
|
ReadBodyStart ();
|
|
if (is_empty)
|
|
throw new InvalidOperationException ("The message body is empty.");
|
|
body_consumed = true;
|
|
return reader;
|
|
}
|
|
|
|
protected override void OnWriteBodyContents (
|
|
XmlDictionaryWriter writer)
|
|
{
|
|
XmlDictionaryReader reader = GetReaderAtBodyContents ();
|
|
while (!reader.EOF && reader.NodeType != XmlNodeType.EndElement)
|
|
writer.WriteNode (reader, false);
|
|
}
|
|
|
|
static readonly char [] whitespaceChars = new char [] {' ', '\t', '\r', '\n'};
|
|
|
|
void ReadEnvelopeStart ()
|
|
{
|
|
reader.MoveToContent ();
|
|
if (reader.IsEmptyElement)
|
|
throw new ArgumentException ("Missing message content XML.");
|
|
reader.ReadStartElement ("Envelope", Version.Envelope.Namespace);
|
|
|
|
// SOAP Header
|
|
reader.MoveToContent ();
|
|
}
|
|
|
|
void ReadHeaders ()
|
|
{
|
|
if (headers != null)
|
|
throw new InvalidOperationException ("XmlReader at headers is already consumed.");
|
|
|
|
string envNS = Version.Envelope.Namespace;
|
|
|
|
headers = new MessageHeaders (version);
|
|
if (reader.LocalName != "Header" || reader.NamespaceURI != envNS)
|
|
return;
|
|
|
|
bool isEmptyHeader = reader.IsEmptyElement;
|
|
reader.ReadStartElement ("Header", envNS);
|
|
reader.MoveToContent ();
|
|
if (isEmptyHeader)
|
|
return;
|
|
|
|
int nHeaders = 0;
|
|
while (!reader.EOF && reader.NodeType != XmlNodeType.EndElement) {
|
|
if (reader.NodeType == XmlNodeType.Element) {
|
|
if (nHeaders++ == max_headers)
|
|
throw new InvalidOperationException (String.Format ("Message header size has exceeded the maximum header size {0}", max_headers));
|
|
headers.Add (new MessageHeader.XmlMessageHeader (reader, Version));
|
|
}
|
|
else
|
|
reader.Skip ();
|
|
// FIXME: handle UnderstoodHeaders as well.
|
|
reader.MoveToContent ();
|
|
}
|
|
reader.ReadEndElement ();
|
|
reader.MoveToContent ();
|
|
}
|
|
|
|
void ReadBodyStart ()
|
|
{
|
|
if (body_consumed)
|
|
throw new InvalidOperationException ("The message body XmlReader is already consumed.");
|
|
|
|
if (body_started)
|
|
return;
|
|
|
|
// read headers in advance.
|
|
if (headers == null)
|
|
ReadHeaders ();
|
|
|
|
// SOAP Body
|
|
body_started = true;
|
|
is_empty = reader.IsEmptyElement;
|
|
attributes = new AttributeCollection ();
|
|
reader.MoveToContent ();
|
|
if (reader.MoveToFirstAttribute ()) {
|
|
do {
|
|
attributes.Add (new AttributeInfo () { Prefix = reader.Prefix, Name = reader.LocalName, Namespace = reader.NamespaceURI, Value = reader.Value});
|
|
} while (reader.MoveToNextAttribute ());
|
|
reader.MoveToElement ();
|
|
}
|
|
reader.ReadStartElement ("Body", Version.Envelope.Namespace);
|
|
if (reader.NodeType == XmlNodeType.EndElement) {
|
|
is_empty = true;
|
|
reader.Read ();
|
|
} else {
|
|
reader.MoveToContent ();
|
|
if (reader.NodeType == XmlNodeType.Element &&
|
|
reader.LocalName == "Fault" &&
|
|
reader.NamespaceURI == Version.Envelope.Namespace)
|
|
is_fault = true;
|
|
}
|
|
}
|
|
|
|
protected override void OnWriteStartBody (
|
|
XmlDictionaryWriter writer)
|
|
{
|
|
ReadBodyStart (); // consume up to attributes
|
|
|
|
base.OnWriteStartBody (writer);
|
|
foreach (var p in attributes)
|
|
writer.WriteAttributeString (p.Prefix, p.Name, p.Namespace, p.Value);
|
|
}
|
|
}
|
|
|
|
internal abstract class MessageImplBase : Message
|
|
{
|
|
MessageHeaders headers;
|
|
MessageProperties properties = new MessageProperties ();
|
|
AttributeCollection attributes;
|
|
|
|
public MessageImplBase (MessageVersion version, string action, AttributeCollection attributes)
|
|
{
|
|
headers = new MessageHeaders (version);
|
|
if (action != null)
|
|
headers.Action = action;
|
|
this.attributes = attributes;
|
|
}
|
|
|
|
internal AttributeCollection Attributes {
|
|
get { return attributes; }
|
|
}
|
|
|
|
public override MessageHeaders Headers {
|
|
get { return headers; }
|
|
}
|
|
|
|
public override MessageProperties Properties {
|
|
get { return properties; }
|
|
}
|
|
|
|
public override MessageVersion Version {
|
|
get { return Headers.MessageVersion; }
|
|
}
|
|
|
|
protected override string OnGetBodyAttribute (
|
|
string localName, string ns)
|
|
{
|
|
var att = attributes.FirstOrDefault (a => a.Name == localName && a.Namespace == ns);
|
|
return att != null ? att.Value : null;
|
|
}
|
|
|
|
protected override void OnWriteStartBody (
|
|
XmlDictionaryWriter writer)
|
|
{
|
|
var dic = Constants.SoapDictionary;
|
|
writer.WriteStartElement ("s", dic.Add ("Body"), dic.Add (Version.Envelope.Namespace));
|
|
foreach (var p in Attributes)
|
|
writer.WriteAttributeString (p.Prefix, p.Name, p.Namespace, p.Value);
|
|
}
|
|
}
|
|
|
|
internal class EmptyMessage : MessageImplBase
|
|
{
|
|
static readonly AttributeCollection empty_attributes = new AttributeCollection ();
|
|
|
|
public EmptyMessage (MessageVersion version, string action)
|
|
: base (version, action, empty_attributes)
|
|
{
|
|
}
|
|
|
|
public override bool IsEmpty {
|
|
get { return true; }
|
|
}
|
|
|
|
protected override void OnWriteBodyContents (
|
|
XmlDictionaryWriter writer)
|
|
{
|
|
}
|
|
|
|
protected override MessageBuffer OnCreateBufferedCopy (
|
|
int maxBufferSize)
|
|
{
|
|
return new DefaultMessageBuffer (Headers, Properties, Attributes);
|
|
}
|
|
}
|
|
|
|
internal class SimpleMessage : MessageImplBase
|
|
{
|
|
BodyWriter body;
|
|
bool is_fault;
|
|
|
|
public SimpleMessage (MessageVersion version,
|
|
string action, BodyWriter body, bool isFault, AttributeCollection attributes)
|
|
: base (version, action, attributes)
|
|
{
|
|
this.body = body;
|
|
this.is_fault = isFault;
|
|
}
|
|
|
|
public override bool IsEmpty {
|
|
get { return false; }
|
|
}
|
|
|
|
public override bool IsFault {
|
|
get { return is_fault; }
|
|
}
|
|
|
|
protected override void OnWriteBodyContents (
|
|
XmlDictionaryWriter writer)
|
|
{
|
|
body.WriteBodyContents (writer);
|
|
}
|
|
|
|
protected override MessageBuffer OnCreateBufferedCopy (
|
|
int maxBufferSize)
|
|
{
|
|
var headers = new MessageHeaders (Headers);
|
|
var props = new MessageProperties (Properties);
|
|
var atts = new AttributeCollection (Attributes);
|
|
return new DefaultMessageBuffer (maxBufferSize, headers, props, body.CreateBufferedCopy (maxBufferSize), IsFault, atts);
|
|
}
|
|
}
|
|
}
|
|
|