You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
@ -0,0 +1,105 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Channels
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime;
|
||||
|
||||
class ByteStreamBufferedMessageData
|
||||
{
|
||||
ArraySegment<byte> buffer;
|
||||
BufferManager bufferManager;
|
||||
int refCount;
|
||||
|
||||
public ByteStreamBufferedMessageData(ArraySegment<byte> buffer)
|
||||
: this(buffer, null)
|
||||
{
|
||||
}
|
||||
|
||||
public ByteStreamBufferedMessageData(ArraySegment<byte> buffer, BufferManager bufferManager)
|
||||
{
|
||||
if (buffer.Array == null)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentNull(SR.ArgumentPropertyShouldNotBeNullError("buffer.Array"));
|
||||
}
|
||||
|
||||
this.buffer = buffer;
|
||||
this.bufferManager = bufferManager;
|
||||
this.refCount = 0;
|
||||
}
|
||||
|
||||
bool IsClosed
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.refCount < 0;
|
||||
}
|
||||
}
|
||||
|
||||
public ArraySegment<byte> Buffer
|
||||
{
|
||||
get
|
||||
{
|
||||
ThrowIfClosed();
|
||||
return this.buffer;
|
||||
}
|
||||
}
|
||||
|
||||
public void Open()
|
||||
{
|
||||
ThrowIfClosed();
|
||||
this.refCount++;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (!this.IsClosed)
|
||||
{
|
||||
if (--this.refCount <= 0)
|
||||
{
|
||||
if (this.bufferManager != null && this.buffer.Array != null)
|
||||
{
|
||||
this.bufferManager.ReturnBuffer(this.buffer.Array);
|
||||
}
|
||||
this.bufferManager = null;
|
||||
this.buffer = default(ArraySegment<byte>);
|
||||
this.refCount = int.MinValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Stream ToStream()
|
||||
{
|
||||
return new ByteStreamBufferedMessageDataStream(this);
|
||||
}
|
||||
|
||||
void ThrowIfClosed()
|
||||
{
|
||||
if (this.IsClosed)
|
||||
{
|
||||
throw FxTrace.Exception.ObjectDisposed(SR.ObjectDisposed(this));
|
||||
}
|
||||
}
|
||||
|
||||
class ByteStreamBufferedMessageDataStream : MemoryStream
|
||||
{
|
||||
ByteStreamBufferedMessageData byteStreamBufferedMessageData;
|
||||
|
||||
public ByteStreamBufferedMessageDataStream(ByteStreamBufferedMessageData byteStreamBufferedMessageData)
|
||||
: base(byteStreamBufferedMessageData.Buffer.Array, byteStreamBufferedMessageData.Buffer.Offset, byteStreamBufferedMessageData.Buffer.Count, false)
|
||||
{
|
||||
this.byteStreamBufferedMessageData = byteStreamBufferedMessageData;
|
||||
this.byteStreamBufferedMessageData.Open(); //increment the refCount
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
this.byteStreamBufferedMessageData.Close();
|
||||
base.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,389 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Channels
|
||||
{
|
||||
using System;
|
||||
using System.Runtime;
|
||||
using System.ServiceModel.Diagnostics;
|
||||
using System.Xml;
|
||||
using System.IO;
|
||||
using System.Runtime.Diagnostics;
|
||||
using SMTD = System.ServiceModel.Diagnostics.Application.TD;
|
||||
using System.Diagnostics;
|
||||
|
||||
class ByteStreamMessageEncoder : MessageEncoder, IStreamedMessageEncoder, IWebMessageEncoderHelper, ITraceSourceStringProvider
|
||||
{
|
||||
string traceSourceString;
|
||||
string maxReceivedMessageSizeExceededResourceString;
|
||||
string maxSentMessageSizeExceededResourceString;
|
||||
XmlDictionaryReaderQuotas quotas;
|
||||
XmlDictionaryReaderQuotas bufferedReadReaderQuotas;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies if this encoder produces Messages that provide a body reader (with the Message.GetReaderAtBodyContents() method) positioned on content.
|
||||
/// See the comments on 'IWebMessageEncoderHelper' for more info.
|
||||
/// </summary>
|
||||
bool moveBodyReaderToContent = false; // false because we want the ByteStreamMessageEncoder to be compatible with previous releases
|
||||
|
||||
public ByteStreamMessageEncoder(XmlDictionaryReaderQuotas quotas)
|
||||
{
|
||||
this.quotas = new XmlDictionaryReaderQuotas();
|
||||
quotas.CopyTo(this.quotas);
|
||||
|
||||
this.bufferedReadReaderQuotas = EncoderHelpers.GetBufferedReadQuotas(this.quotas);
|
||||
|
||||
this.maxSentMessageSizeExceededResourceString = SR.MaxSentMessageSizeExceeded("{0}");
|
||||
this.maxReceivedMessageSizeExceededResourceString = SR.MaxReceivedMessageSizeExceeded("{0}");
|
||||
}
|
||||
|
||||
void IWebMessageEncoderHelper.EnableBodyReaderMoveToContent()
|
||||
{
|
||||
this.moveBodyReaderToContent = true;
|
||||
}
|
||||
|
||||
public override string ContentType
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public override string MediaType
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public override MessageVersion MessageVersion
|
||||
{
|
||||
get { return MessageVersion.None; }
|
||||
}
|
||||
|
||||
public override bool IsContentTypeSupported(string contentType)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType)
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentNull("stream");
|
||||
}
|
||||
|
||||
if (TD.ByteStreamMessageDecodingStartIsEnabled())
|
||||
{
|
||||
TD.ByteStreamMessageDecodingStart();
|
||||
}
|
||||
|
||||
Message message = ByteStreamMessage.CreateMessage(stream, this.quotas, this.moveBodyReaderToContent);
|
||||
message.Properties.Encoder = this;
|
||||
|
||||
if (SMTD.StreamedMessageReadByEncoderIsEnabled())
|
||||
{
|
||||
SMTD.StreamedMessageReadByEncoder(EventTraceActivityHelper.TryExtractActivity(message, true));
|
||||
}
|
||||
|
||||
if (MessageLogger.LogMessagesAtTransportLevel)
|
||||
{
|
||||
MessageLogger.LogMessage(ref message, MessageLoggingSource.TransportReceive);
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType)
|
||||
{
|
||||
if (buffer.Array == null)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentNull("buffer.Array");
|
||||
}
|
||||
|
||||
if (bufferManager == null)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentNull("bufferManager");
|
||||
}
|
||||
|
||||
if (TD.ByteStreamMessageDecodingStartIsEnabled())
|
||||
{
|
||||
TD.ByteStreamMessageDecodingStart();
|
||||
}
|
||||
|
||||
ByteStreamBufferedMessageData messageData = new ByteStreamBufferedMessageData(buffer, bufferManager);
|
||||
|
||||
Message message = ByteStreamMessage.CreateMessage(messageData, this.bufferedReadReaderQuotas, this.moveBodyReaderToContent);
|
||||
message.Properties.Encoder = this;
|
||||
|
||||
if (SMTD.MessageReadByEncoderIsEnabled())
|
||||
{
|
||||
SMTD.MessageReadByEncoder(
|
||||
EventTraceActivityHelper.TryExtractActivity(message, true),
|
||||
buffer.Count,
|
||||
this);
|
||||
}
|
||||
|
||||
if (MessageLogger.LogMessagesAtTransportLevel)
|
||||
{
|
||||
MessageLogger.LogMessage(ref message, MessageLoggingSource.TransportReceive);
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public override void WriteMessage(Message message, Stream stream)
|
||||
{
|
||||
if (message == null)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentNull("message");
|
||||
}
|
||||
if (stream == null)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentNull("stream");
|
||||
}
|
||||
|
||||
ThrowIfMismatchedMessageVersion(message);
|
||||
|
||||
EventTraceActivity eventTraceActivity = null;
|
||||
if (TD.ByteStreamMessageEncodingStartIsEnabled())
|
||||
{
|
||||
eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message);
|
||||
TD.ByteStreamMessageEncodingStart(eventTraceActivity);
|
||||
}
|
||||
|
||||
message.Properties.Encoder = this;
|
||||
|
||||
if (MessageLogger.LogMessagesAtTransportLevel)
|
||||
{
|
||||
MessageLogger.LogMessage(ref message, MessageLoggingSource.TransportSend);
|
||||
}
|
||||
|
||||
using (XmlWriter writer = new XmlByteStreamWriter(stream, false))
|
||||
{
|
||||
message.WriteMessage(writer);
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
if (SMTD.StreamedMessageWrittenByEncoderIsEnabled())
|
||||
{
|
||||
SMTD.StreamedMessageWrittenByEncoder(eventTraceActivity ?? EventTraceActivityHelper.TryExtractActivity(message));
|
||||
}
|
||||
}
|
||||
|
||||
public override IAsyncResult BeginWriteMessage(Message message, Stream stream, AsyncCallback callback, object state)
|
||||
{
|
||||
if (message == null)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentNull("message");
|
||||
}
|
||||
if (stream == null)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentNull("stream");
|
||||
}
|
||||
|
||||
ThrowIfMismatchedMessageVersion(message);
|
||||
message.Properties.Encoder = this;
|
||||
|
||||
if (MessageLogger.LogMessagesAtTransportLevel)
|
||||
{
|
||||
MessageLogger.LogMessage(ref message, MessageLoggingSource.TransportSend);
|
||||
}
|
||||
|
||||
return new WriteMessageAsyncResult(message, stream, callback, state);
|
||||
}
|
||||
|
||||
public override void EndWriteMessage(IAsyncResult result)
|
||||
{
|
||||
WriteMessageAsyncResult.End(result);
|
||||
}
|
||||
|
||||
public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
|
||||
{
|
||||
if (message == null)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentNull("message");
|
||||
}
|
||||
if (bufferManager == null)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentNull("bufferManager");
|
||||
}
|
||||
if (maxMessageSize < 0)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentOutOfRange("maxMessageSize", maxMessageSize, SR.ArgumentOutOfMinRange(0));
|
||||
}
|
||||
if (messageOffset < 0)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentOutOfRange("messageOffset", messageOffset, SR.ArgumentOutOfMinRange(0));
|
||||
}
|
||||
|
||||
EventTraceActivity eventTraceActivity = null;
|
||||
if (TD.ByteStreamMessageEncodingStartIsEnabled())
|
||||
{
|
||||
eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message);
|
||||
TD.ByteStreamMessageEncodingStart(eventTraceActivity);
|
||||
}
|
||||
|
||||
ThrowIfMismatchedMessageVersion(message);
|
||||
message.Properties.Encoder = this;
|
||||
|
||||
ArraySegment<byte> messageBuffer;
|
||||
int size;
|
||||
|
||||
using (BufferManagerOutputStream stream = new BufferManagerOutputStream(maxSentMessageSizeExceededResourceString, 0, maxMessageSize, bufferManager))
|
||||
{
|
||||
stream.Skip(messageOffset);
|
||||
using (XmlWriter writer = new XmlByteStreamWriter(stream, true))
|
||||
{
|
||||
message.WriteMessage(writer);
|
||||
writer.Flush();
|
||||
byte[] bytes = stream.ToArray(out size);
|
||||
messageBuffer = new ArraySegment<byte>(bytes, messageOffset, size - messageOffset);
|
||||
}
|
||||
}
|
||||
|
||||
if (SMTD.MessageWrittenByEncoderIsEnabled())
|
||||
{
|
||||
SMTD.MessageWrittenByEncoder(
|
||||
eventTraceActivity ?? EventTraceActivityHelper.TryExtractActivity(message),
|
||||
messageBuffer.Count,
|
||||
this);
|
||||
}
|
||||
|
||||
if (MessageLogger.LogMessagesAtTransportLevel)
|
||||
{
|
||||
// DevDiv#486728
|
||||
// Don't pass in a buffer manager to avoid returning 'messageBuffer" to the bufferManager twice.
|
||||
ByteStreamBufferedMessageData messageData = new ByteStreamBufferedMessageData(messageBuffer, null);
|
||||
using (XmlReader reader = new XmlBufferedByteStreamReader(messageData, this.quotas))
|
||||
{
|
||||
MessageLogger.LogMessage(ref message, reader, MessageLoggingSource.TransportSend);
|
||||
}
|
||||
}
|
||||
|
||||
return messageBuffer;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return ByteStreamMessageUtility.EncoderName;
|
||||
}
|
||||
|
||||
public Stream GetResponseMessageStream(Message message)
|
||||
{
|
||||
if (message == null)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentNull("message");
|
||||
}
|
||||
|
||||
ThrowIfMismatchedMessageVersion(message);
|
||||
|
||||
if (!ByteStreamMessage.IsInternalByteStreamMessage(message))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return message.GetBody<Stream>();
|
||||
}
|
||||
|
||||
string ITraceSourceStringProvider.GetSourceString()
|
||||
{
|
||||
// Other MessageEncoders use base.GetTraceSourceString but that would require a public api change in MessageEncoder
|
||||
// as ByteStreamMessageEncoder is in a different assemly. The same logic is reimplemented here.
|
||||
if (this.traceSourceString == null)
|
||||
{
|
||||
this.traceSourceString = DiagnosticTraceBase.CreateDefaultSourceString(this);
|
||||
}
|
||||
|
||||
return this.traceSourceString;
|
||||
}
|
||||
|
||||
class WriteMessageAsyncResult : AsyncResult
|
||||
{
|
||||
Message message;
|
||||
Stream stream;
|
||||
static Action<IAsyncResult, Exception> onCleanup;
|
||||
XmlByteStreamWriter writer;
|
||||
EventTraceActivity eventTraceActivity;
|
||||
|
||||
public WriteMessageAsyncResult(Message message, Stream stream, AsyncCallback callback, object state)
|
||||
: base(callback, state)
|
||||
{
|
||||
this.message = message;
|
||||
this.stream = stream;
|
||||
this.writer = new XmlByteStreamWriter(stream, false);
|
||||
if (onCleanup == null)
|
||||
{
|
||||
onCleanup = new Action<IAsyncResult, Exception>(Cleanup);
|
||||
}
|
||||
this.OnCompleting += onCleanup;
|
||||
Exception completionException = null;
|
||||
bool completeSelf = false;
|
||||
|
||||
this.eventTraceActivity = null;
|
||||
if (TD.ByteStreamMessageEncodingStartIsEnabled())
|
||||
{
|
||||
this.eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message);
|
||||
TD.ByteStreamMessageEncodingStart(this.eventTraceActivity);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
IAsyncResult result = message.BeginWriteMessage(writer, PrepareAsyncCompletion(HandleWriteMessage), this);
|
||||
completeSelf = SyncContinue(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (Fx.IsFatal(ex))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
completeSelf = true;
|
||||
completionException = ex;
|
||||
}
|
||||
|
||||
if (completeSelf)
|
||||
{
|
||||
this.Complete(true, completionException);
|
||||
}
|
||||
}
|
||||
|
||||
static bool HandleWriteMessage(IAsyncResult result)
|
||||
{
|
||||
WriteMessageAsyncResult thisPtr = (WriteMessageAsyncResult)result.AsyncState;
|
||||
|
||||
thisPtr.message.EndWriteMessage(result);
|
||||
thisPtr.writer.Flush();
|
||||
|
||||
if (SMTD.MessageWrittenAsynchronouslyByEncoderIsEnabled())
|
||||
{
|
||||
SMTD.MessageWrittenAsynchronouslyByEncoder(
|
||||
thisPtr.eventTraceActivity ?? EventTraceActivityHelper.TryExtractActivity(thisPtr.message));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void Cleanup(IAsyncResult result, Exception ex)
|
||||
{
|
||||
WriteMessageAsyncResult thisPtr = (WriteMessageAsyncResult)result;
|
||||
bool success = false;
|
||||
try
|
||||
{
|
||||
thisPtr.writer.Close();
|
||||
success = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!success)
|
||||
{
|
||||
FxTrace.Exception.TraceHandledException(ex, TraceEventType.Information);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void End(IAsyncResult result)
|
||||
{
|
||||
AsyncResult.End<WriteMessageAsyncResult>(result);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Channels
|
||||
{
|
||||
using System.Runtime;
|
||||
using System.Xml;
|
||||
|
||||
class ByteStreamMessageEncoderFactory : MessageEncoderFactory
|
||||
{
|
||||
ByteStreamMessageEncoder encoder;
|
||||
|
||||
public ByteStreamMessageEncoderFactory(XmlDictionaryReaderQuotas quotas)
|
||||
{
|
||||
this.encoder = new ByteStreamMessageEncoder(quotas);
|
||||
}
|
||||
|
||||
public override MessageEncoder Encoder
|
||||
{
|
||||
get { return this.encoder; }
|
||||
}
|
||||
|
||||
public override MessageVersion MessageVersion
|
||||
{
|
||||
get { return encoder.MessageVersion; }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Channels
|
||||
{
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Runtime;
|
||||
using System.Xml;
|
||||
|
||||
[Fx.Tag.XamlVisible(false)]
|
||||
public sealed class ByteStreamMessageEncodingBindingElement : MessageEncodingBindingElement
|
||||
{
|
||||
XmlDictionaryReaderQuotas readerQuotas;
|
||||
|
||||
public ByteStreamMessageEncodingBindingElement() : this((XmlDictionaryReaderQuotas)null)
|
||||
{
|
||||
}
|
||||
|
||||
public ByteStreamMessageEncodingBindingElement(XmlDictionaryReaderQuotas quota)
|
||||
{
|
||||
this.readerQuotas = new XmlDictionaryReaderQuotas();
|
||||
if (quota != null)
|
||||
{
|
||||
quota.CopyTo(this.readerQuotas);
|
||||
}
|
||||
}
|
||||
|
||||
ByteStreamMessageEncodingBindingElement(ByteStreamMessageEncodingBindingElement byteStreamEncoderBindingElement)
|
||||
: this(byteStreamEncoderBindingElement.readerQuotas)
|
||||
{
|
||||
}
|
||||
|
||||
public override MessageVersion MessageVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
return MessageVersion.None;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != MessageVersion.None)
|
||||
{
|
||||
throw FxTrace.Exception.Argument("MessageVersion", SR.ByteStreamMessageEncoderMessageVersionNotSupported(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public XmlDictionaryReaderQuotas ReaderQuotas
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.readerQuotas;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
throw FxTrace.Exception.ArgumentNull("ReaderQuotas");
|
||||
value.CopyTo(this.ReaderQuotas);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanBuildChannelFactory<TChannel>(BindingContext context)
|
||||
{
|
||||
return InternalCanBuildChannelFactory<TChannel>(context);
|
||||
}
|
||||
|
||||
public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
|
||||
{
|
||||
return InternalBuildChannelFactory<TChannel>(context);
|
||||
}
|
||||
|
||||
public override bool CanBuildChannelListener<TChannel>(BindingContext context)
|
||||
{
|
||||
return InternalCanBuildChannelListener<TChannel>(context);
|
||||
}
|
||||
|
||||
public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context)
|
||||
{
|
||||
return InternalBuildChannelListener<TChannel>(context);
|
||||
}
|
||||
|
||||
public override MessageEncoderFactory CreateMessageEncoderFactory()
|
||||
{
|
||||
return new ByteStreamMessageEncoderFactory(this.readerQuotas);
|
||||
}
|
||||
|
||||
public override BindingElement Clone()
|
||||
{
|
||||
return new ByteStreamMessageEncodingBindingElement(this);
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public bool ShouldSerializeMessageVersion()
|
||||
{
|
||||
// Always MessageVersion.None in ByteStreamMessageEncoder
|
||||
return false;
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public bool ShouldSerializeReaderQuotas()
|
||||
{
|
||||
return (!EncoderDefaults.IsDefaultReaderQuotas(this.ReaderQuotas));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Channels
|
||||
{
|
||||
using System.Xml;
|
||||
|
||||
static class ByteStreamMessageUtility
|
||||
{
|
||||
public const string StreamElementName = "Binary";
|
||||
public const string XmlNamespace = "http://www.w3.org/XML/1998/namespace";
|
||||
public const string XmlNamespaceNamespace = "http://www.w3.org/2000/xmlns/";
|
||||
|
||||
// used when doing message tracing
|
||||
internal const string EncoderName = "ByteStreamMessageEncoder";
|
||||
|
||||
internal static void EnsureByteBoundaries(byte[] buffer, int index, int count, bool isRead)
|
||||
{
|
||||
if (buffer == null)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentNull("buffer");
|
||||
}
|
||||
if (index < 0)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentOutOfRange("index", index, SR.ArgumentOutOfMinRange(0));
|
||||
}
|
||||
// we explicitly allow the case for index = 0, buffer.Length = 0 and count = 0 when it is write
|
||||
// Note that we rely on the last check of count > buffer.Length - index to cover count > 0 && index == buffer.Length case
|
||||
if (index > buffer.Length || (isRead && index == buffer.Length))
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentOutOfRange("index", index, SR.OffsetExceedsBufferSize(buffer.Length));
|
||||
}
|
||||
if (count < 0)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentOutOfRange("count", count, SR.ArgumentOutOfMinRange(0));
|
||||
}
|
||||
if (count > buffer.Length - index)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentOutOfRange("count", count, SR.SizeExceedsRemainingBufferSpace(buffer.Length - index));
|
||||
}
|
||||
}
|
||||
|
||||
internal static XmlDictionaryReaderQuotas EnsureQuotas(XmlDictionaryReaderQuotas quotas)
|
||||
{
|
||||
return quotas ?? EncoderDefaults.ReaderQuotas;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
// <copyright>
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace System.ServiceModel.Channels
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime;
|
||||
using System.Runtime.Diagnostics;
|
||||
using System.ServiceModel.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Xml;
|
||||
|
||||
internal class ClientUdpOutputChannel : UdpOutputChannel
|
||||
{
|
||||
private EndpointAddress to;
|
||||
|
||||
public ClientUdpOutputChannel(ChannelManagerBase factory, IPEndPoint remoteEndPoint, MessageEncoder encoder, BufferManager bufferManager, UdpSocket[] sendSockets, UdpRetransmissionSettings retransmissionSettings, EndpointAddress to, Uri via, bool isMulticast)
|
||||
: base(factory, encoder, bufferManager, sendSockets, retransmissionSettings, via, isMulticast)
|
||||
{
|
||||
Fx.Assert(to != null, "to address can't be null for this constructor...");
|
||||
Fx.Assert(remoteEndPoint != null, "remoteEndPoint can't be null");
|
||||
|
||||
this.RemoteEndPoint = remoteEndPoint;
|
||||
this.to = to;
|
||||
}
|
||||
|
||||
public IPEndPoint RemoteEndPoint
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
protected override UdpSocket[] GetSendSockets(Message message, out IPEndPoint remoteEndPoint, out Exception exceptionToBeThrown)
|
||||
{
|
||||
UdpSocket[] socketList = null;
|
||||
remoteEndPoint = this.RemoteEndPoint;
|
||||
exceptionToBeThrown = null;
|
||||
|
||||
if (this.IsMulticast)
|
||||
{
|
||||
// always send on all sockets...
|
||||
socketList = this.SendSockets;
|
||||
}
|
||||
else
|
||||
{
|
||||
Fx.Assert(this.SendSockets.Length == 1, "Unicast Send socket list on client should always be 1 item long");
|
||||
socketList = this.SendSockets;
|
||||
}
|
||||
|
||||
return socketList;
|
||||
}
|
||||
|
||||
protected override void AddHeadersTo(Message message)
|
||||
{
|
||||
Fx.Assert(message != null, "Message can't be null");
|
||||
|
||||
if (message.Version.Addressing != AddressingVersion.None)
|
||||
{
|
||||
this.to.ApplyTo(message);
|
||||
}
|
||||
|
||||
message.Properties.Via = this.Via;
|
||||
|
||||
base.AddHeadersTo(message);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
//------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Channels
|
||||
{
|
||||
using System;
|
||||
using System.Runtime;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
sealed class DuplicateMessageDetector : IDisposable
|
||||
{
|
||||
HashAlgorithm hashAlgorithm;
|
||||
|
||||
[Fx.Tag.Cache(typeof(string), Fx.Tag.CacheAttrition.PartialPurgeOnEachAccess, SizeLimit = "maxListLength parameter to constructor")]
|
||||
DuplicateDetector<string> duplicateDetector;
|
||||
[Fx.Tag.SynchronizationObject()]
|
||||
object thisLock;
|
||||
bool disposed;
|
||||
|
||||
public DuplicateMessageDetector(int maxListLength)
|
||||
{
|
||||
Fx.Assert(maxListLength > 0, "maxListLength must be > 0");
|
||||
|
||||
this.disposed = false;
|
||||
this.hashAlgorithm = HashAlgorithm.Create();
|
||||
this.thisLock = new object();
|
||||
|
||||
this.duplicateDetector = new DuplicateDetector<string>(maxListLength);
|
||||
}
|
||||
|
||||
public bool IsDuplicate(ArraySegment<byte> msgBytes, out string hashString)
|
||||
{
|
||||
Fx.Assert(msgBytes != null, "messageBytes can't be null");
|
||||
Fx.Assert(msgBytes.Count > 0, "messageBytes.Count must be > 0");
|
||||
|
||||
byte[] hash;
|
||||
|
||||
bool notDuplicate = true;
|
||||
lock (this.thisLock)
|
||||
{
|
||||
if (disposed)
|
||||
{
|
||||
throw FxTrace.Exception.AsError(new ObjectDisposedException(this.GetType().ToString()));
|
||||
}
|
||||
|
||||
hash = this.hashAlgorithm.ComputeHash(msgBytes.Array, msgBytes.Offset, msgBytes.Count);
|
||||
}
|
||||
|
||||
hashString = Convert.ToBase64String(hash);
|
||||
|
||||
Fx.Assert(string.IsNullOrEmpty(hashString) == false, "computed hashstring is null or empty");
|
||||
|
||||
lock (this.thisLock)
|
||||
{
|
||||
//the act of retreiving an existing item pushes it to the front of the MRU list, ensuring
|
||||
//that the oldest hashes are trimmed first when we hit the max length.
|
||||
notDuplicate = this.duplicateDetector.AddIfNotDuplicate(hashString);
|
||||
}
|
||||
|
||||
return !notDuplicate;
|
||||
}
|
||||
|
||||
public void RemoveEntry(string msgHash)
|
||||
{
|
||||
Fx.Assert(!string.IsNullOrEmpty(msgHash), "Message hash should never be null or empty");
|
||||
|
||||
lock (this.thisLock)
|
||||
{
|
||||
if (this.disposed)
|
||||
{
|
||||
throw FxTrace.Exception.AsError(new ObjectDisposedException(this.GetType().ToString()));
|
||||
}
|
||||
|
||||
this.duplicateDetector.Remove(msgHash);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (this.disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (this.thisLock)
|
||||
{
|
||||
if (!this.disposed)
|
||||
{
|
||||
this.disposed = true;
|
||||
if (this.duplicateDetector != null)
|
||||
{
|
||||
this.duplicateDetector.Clear();
|
||||
}
|
||||
|
||||
this.hashAlgorithm.Clear();
|
||||
this.hashAlgorithm = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
// <copyright>
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace System.ServiceModel.Channels
|
||||
{
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Runtime;
|
||||
|
||||
/// <summary>
|
||||
/// A static extension methods class for getting a <see cref="Message"/> instance
|
||||
/// from an <see cref="HttpRequestMessage"/> instance.
|
||||
/// </summary>
|
||||
public static class HttpRequestMessageExtensionMethods
|
||||
{
|
||||
/// <summary>
|
||||
/// An extension method for getting a <see cref="Message"/> instance
|
||||
/// from an <see cref="HttpRequestMessage"/> instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The <see cref="Message"/> instance can be read, written and copied
|
||||
/// just as a traditional <see cref="ByteStreamMessage"/> instance. The
|
||||
/// <see cref="Message"/> instance can also "read" to retrieve the original
|
||||
/// <see cref="HttpRequestMessage"/> instance by calling the
|
||||
/// <see cref="MessageExtensionMethods.ToHttpRequestMessage">
|
||||
/// Message.ToHttpRequestMessage()</see> extension method.
|
||||
/// </remarks>
|
||||
/// <param name="httpRequestMessage">The <see cref="HttpRequestMessage"/>
|
||||
/// from which to create the <see cref="Message"/> instance.</param>
|
||||
/// <returns>The new <see cref="Message"/> instance.</returns>
|
||||
public static Message ToMessage(this HttpRequestMessage httpRequestMessage)
|
||||
{
|
||||
if (httpRequestMessage == null)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentNull("httpRequestMessage");
|
||||
}
|
||||
|
||||
Message message = ByteStreamMessage.CreateMessage(httpRequestMessage, null);
|
||||
message.ConfigureAsHttpMessage(httpRequestMessage);
|
||||
|
||||
return message;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
// <copyright>
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace System.ServiceModel.Channels
|
||||
{
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Runtime;
|
||||
|
||||
/// <summary>
|
||||
/// A static extension methods class for getting a <see cref="Message"/> instance
|
||||
/// from an <see cref="HttpResponseMessage"/> instance.
|
||||
/// </summary>
|
||||
public static class HttpResponseMessageExtensionMethods
|
||||
{
|
||||
/// <summary>
|
||||
/// An extension method for getting a <see cref="Message"/> instance
|
||||
/// from an <see cref="HttpResponseMessage"/> instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The <see cref="Message"/> instance can be read, written and copied
|
||||
/// just as a traditional <see cref="ByteStreamMessage"/> instance. The
|
||||
/// <see cref="Message"/> instance can also "read" to retrieve the original
|
||||
/// <see cref="HttpResponseMessage"/> instance by calling the
|
||||
/// <see cref="MessageExtensionMethods.ToHttpResponseMessage">
|
||||
/// Message.ToHttpResponseMessage()</see> extension method.
|
||||
/// </remarks>
|
||||
/// <param name="httpResponseMessage">The <see cref="HttpResponseMessage"/>
|
||||
/// from which to create the <see cref="Message"/> instance.</param>
|
||||
/// <returns>The new <see cref="Message"/> instance.</returns>
|
||||
public static Message ToMessage(this HttpResponseMessage httpResponseMessage)
|
||||
{
|
||||
if (httpResponseMessage == null)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentNull("httpResponseMessage");
|
||||
}
|
||||
|
||||
Message message = ByteStreamMessage.CreateMessage(httpResponseMessage, null);
|
||||
message.ConfigureAsHttpMessage(httpResponseMessage);
|
||||
|
||||
return message;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
//----------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//----------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Channels
|
||||
{
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Collections.Generic;
|
||||
|
||||
interface IUdpReceiveHandler
|
||||
{
|
||||
int MaxReceivedMessageSize { get; }
|
||||
void HandleAsyncException(Exception exception);
|
||||
|
||||
//returns false if the message was dropped because the max pending message count was hit.
|
||||
bool HandleDataReceived(ArraySegment<byte> data, EndPoint remoteEndpoint, int interfaceIndex, Action onMessageDequeuedCallback);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,201 @@
|
||||
// <copyright>
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace System.ServiceModel.Channels
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Runtime;
|
||||
|
||||
/// <summary>
|
||||
/// A static extension methods class for getting either an <see cref="HttpRequestMessage"/>
|
||||
/// or <see cref="HttpResponseMessage"/> instance from a <see cref="Message"/> instance.
|
||||
/// </summary>
|
||||
public static class MessageExtensionMethods
|
||||
{
|
||||
private const string MessageHeadersPropertyKey = "System.ServiceModel.Channels.MessageHeaders";
|
||||
private const string ToHttpRequestMessageMethodName = "ToHttpRequestMessage()";
|
||||
private const string ToHttpResponseMessageMethodName = "ToHttpResponseMessage()";
|
||||
private static readonly string HttpRequestMessagePropertyTypeName = typeof(HttpRequestMessageProperty).Name;
|
||||
private static readonly string HttpResponseMessagePropertyTypeName = typeof(HttpResponseMessageProperty).Name;
|
||||
|
||||
/// <summary>
|
||||
/// An extension method for getting a <see cref="HttpRequestMessage"/> instance
|
||||
/// from an <see cref="Message"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="message">The <see cref="Message"/> instance from which to
|
||||
/// get the <see cref="HttpRequestMessage"/> instance.</param>
|
||||
/// <returns>The <see cref="HttpRequestMessage"/> instance.</returns>
|
||||
public static HttpRequestMessage ToHttpRequestMessage(this Message message)
|
||||
{
|
||||
if (message == null)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentNull("message");
|
||||
}
|
||||
|
||||
HttpRequestMessage httpRequestMessage = HttpRequestMessageProperty.GetHttpRequestMessageFromMessage(message);
|
||||
if (httpRequestMessage == null)
|
||||
{
|
||||
HttpRequestMessageProperty requestMessageProperty = message.Properties.GetValue<HttpRequestMessageProperty>(HttpRequestMessageProperty.Name);
|
||||
if (requestMessageProperty == null)
|
||||
{
|
||||
throw FxTrace.Exception.AsError(
|
||||
new InvalidOperationException(
|
||||
SR.MissingHttpMessageProperty(
|
||||
ToHttpRequestMessageMethodName,
|
||||
HttpRequestMessagePropertyTypeName)));
|
||||
}
|
||||
|
||||
httpRequestMessage = CreateRequestMessage(message, requestMessageProperty);
|
||||
}
|
||||
|
||||
return httpRequestMessage;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An extension method for getting a <see cref="HttpResponseMessage"/> instance
|
||||
/// from an <see cref="Message"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="message">The <see cref="Message"/> instance from which to
|
||||
/// get the <see cref="HttpResponseMessage"/> instance.</param>
|
||||
/// <returns>The <see cref="HttpResponseMessage"/> instance.</returns>
|
||||
public static HttpResponseMessage ToHttpResponseMessage(this Message message)
|
||||
{
|
||||
if (message == null)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentNull("message");
|
||||
}
|
||||
|
||||
HttpResponseMessage httpResponseMessage = HttpResponseMessageProperty.GetHttpResponseMessageFromMessage(message);
|
||||
if (httpResponseMessage == null)
|
||||
{
|
||||
HttpResponseMessageProperty responseMessageProperty = message.Properties.GetValue<HttpResponseMessageProperty>(HttpResponseMessageProperty.Name);
|
||||
if (responseMessageProperty == null)
|
||||
{
|
||||
throw FxTrace.Exception.AsError(
|
||||
new InvalidOperationException(
|
||||
SR.MissingHttpMessageProperty(
|
||||
ToHttpResponseMessageMethodName,
|
||||
HttpResponseMessagePropertyTypeName)));
|
||||
}
|
||||
|
||||
httpResponseMessage = CreateResponseMessage(message, responseMessageProperty);
|
||||
}
|
||||
|
||||
return httpResponseMessage;
|
||||
}
|
||||
|
||||
internal static void ConfigureAsHttpMessage(this Message message, HttpRequestMessage httpRequestMessage)
|
||||
{
|
||||
Fx.Assert(message != null, "The 'message' parameter should never be null.");
|
||||
Fx.Assert(httpRequestMessage != null, "The 'httpRequestMessage' parameter should never be null.");
|
||||
|
||||
message.Properties.Add(HttpRequestMessageProperty.Name, new HttpRequestMessageProperty(httpRequestMessage));
|
||||
CopyPropertiesToMessage(message, httpRequestMessage.Properties);
|
||||
}
|
||||
|
||||
internal static void ConfigureAsHttpMessage(this Message message, HttpResponseMessage httpResponseMessage)
|
||||
{
|
||||
Fx.Assert(message != null, "The 'message' parameter should never be null.");
|
||||
Fx.Assert(httpResponseMessage != null, "The 'httpResponseMessage' parameter should never be null.");
|
||||
|
||||
message.Properties.Add(HttpResponseMessageProperty.Name, new HttpResponseMessageProperty(httpResponseMessage));
|
||||
HttpRequestMessage httpRequestMessage = httpResponseMessage.RequestMessage;
|
||||
if (httpRequestMessage != null)
|
||||
{
|
||||
CopyPropertiesToMessage(message, httpRequestMessage.Properties);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CopyPropertiesToMessage(Message message, IDictionary<string, object> properties)
|
||||
{
|
||||
Fx.Assert(message != null, "The 'message' parameter should not be null.");
|
||||
Fx.Assert(properties != null, "The 'properties' parameter should not be null.");
|
||||
|
||||
foreach (KeyValuePair<string, object> property in properties)
|
||||
{
|
||||
MessageHeaders messageHeaders = property.Value as MessageHeaders;
|
||||
if (messageHeaders != null &&
|
||||
messageHeaders.MessageVersion == MessageVersion.None &&
|
||||
string.Equals(property.Key, MessageHeadersPropertyKey, StringComparison.Ordinal))
|
||||
{
|
||||
foreach (MessageHeader header in messageHeaders)
|
||||
{
|
||||
message.Headers.Add(header);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
message.Properties.Add(property.Key, property.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static HttpRequestMessage CreateRequestMessage(Message message, HttpRequestMessageProperty requestMessageProperty)
|
||||
{
|
||||
Fx.Assert(message != null, "The 'message' parameter should not be null.");
|
||||
Fx.Assert(requestMessageProperty != null, "The 'requestMessageProperty' parameter should not be null.");
|
||||
|
||||
HttpRequestMessage request = new HttpRequestMessage();
|
||||
request.RequestUri = message.Properties.Via;
|
||||
|
||||
Fx.Assert(requestMessageProperty.Method != null, "The HttpRequestMessageProperty class ensures the 'Method' property will never be null.");
|
||||
request.Method = new HttpMethod(requestMessageProperty.Method);
|
||||
|
||||
request.Content = CreateMessageContent(message, requestMessageProperty.SuppressEntityBody);
|
||||
|
||||
WebHeaderCollection headers = requestMessageProperty.Headers;
|
||||
foreach (string headerKey in headers.AllKeys)
|
||||
{
|
||||
request.AddHeader(headerKey, headers[headerKey]);
|
||||
}
|
||||
|
||||
request.CopyPropertiesFromMessage(message);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
private static HttpResponseMessage CreateResponseMessage(Message message, HttpResponseMessageProperty responseMessageProperty)
|
||||
{
|
||||
Fx.Assert(message != null, "The 'message' parameter should not be null.");
|
||||
Fx.Assert(responseMessageProperty != null, "The 'responseMessageProperty' parameter should not be null.");
|
||||
|
||||
HttpResponseMessage response = new HttpResponseMessage();
|
||||
response.StatusCode = responseMessageProperty.HasStatusCodeBeenSet ?
|
||||
responseMessageProperty.StatusCode :
|
||||
message.IsFault ? HttpStatusCode.InternalServerError : HttpStatusCode.OK;
|
||||
|
||||
string reasonPhrase = responseMessageProperty.StatusDescription;
|
||||
if (!string.IsNullOrEmpty(reasonPhrase))
|
||||
{
|
||||
response.ReasonPhrase = reasonPhrase;
|
||||
}
|
||||
|
||||
response.Content = CreateMessageContent(message, responseMessageProperty.SuppressEntityBody);
|
||||
|
||||
WebHeaderCollection headers = responseMessageProperty.Headers;
|
||||
foreach (string headerKey in headers.AllKeys)
|
||||
{
|
||||
response.AddHeader(headerKey, headers[headerKey]);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private static HttpContent CreateMessageContent(Message message, bool suppressEntityBody)
|
||||
{
|
||||
Fx.Assert(message != null, "The 'message' parameter should not be null.");
|
||||
|
||||
if (suppressEntityBody || message.IsEmpty)
|
||||
{
|
||||
return new ByteArrayContent(EmptyArray<byte>.Instance);
|
||||
}
|
||||
|
||||
return new StreamContent(message.GetBody<Stream>());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Channels
|
||||
{
|
||||
using System.Runtime;
|
||||
|
||||
public class NetworkInterfaceMessageProperty
|
||||
{
|
||||
const string PropertyName = "NetworkInterfaceMessageProperty";
|
||||
|
||||
public NetworkInterfaceMessageProperty(int interfaceIndex)
|
||||
{
|
||||
this.InterfaceIndex = interfaceIndex;
|
||||
}
|
||||
|
||||
NetworkInterfaceMessageProperty(NetworkInterfaceMessageProperty other)
|
||||
{
|
||||
this.InterfaceIndex = other.InterfaceIndex;
|
||||
}
|
||||
|
||||
public static string Name
|
||||
{
|
||||
get { return PropertyName; }
|
||||
}
|
||||
|
||||
public int InterfaceIndex
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public static bool TryGet(Message message, out NetworkInterfaceMessageProperty property)
|
||||
{
|
||||
if (message == null)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentNull("message");
|
||||
}
|
||||
|
||||
return TryGet(message.Properties, out property);
|
||||
}
|
||||
|
||||
public static bool TryGet(MessageProperties properties, out NetworkInterfaceMessageProperty property)
|
||||
{
|
||||
if (properties == null)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentNull("properties");
|
||||
}
|
||||
|
||||
object value = null;
|
||||
if (properties.TryGetValue(PropertyName, out value))
|
||||
{
|
||||
property = value as NetworkInterfaceMessageProperty;
|
||||
}
|
||||
else
|
||||
{
|
||||
property = null;
|
||||
}
|
||||
return property != null;
|
||||
}
|
||||
|
||||
public void AddTo(Message message)
|
||||
{
|
||||
if (message == null)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentNull("message");
|
||||
}
|
||||
|
||||
AddTo(message.Properties);
|
||||
}
|
||||
|
||||
public void AddTo(MessageProperties properties)
|
||||
{
|
||||
if (properties == null)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentNull("properties");
|
||||
}
|
||||
|
||||
properties.Add(NetworkInterfaceMessageProperty.Name, this);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,304 @@
|
||||
// <copyright>
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace System.ServiceModel.Channels
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime;
|
||||
using System.Runtime.Diagnostics;
|
||||
using System.ServiceModel.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Xml;
|
||||
|
||||
internal class ServerUdpOutputChannel : UdpOutputChannel
|
||||
{
|
||||
public ServerUdpOutputChannel(ChannelManagerBase factory, MessageEncoder encoder, BufferManager bufferManager, UdpSocket[] sendSockets, UdpRetransmissionSettings retransmissionSettings, Uri via, bool isMulticast)
|
||||
: base(factory, encoder, bufferManager, sendSockets, retransmissionSettings, via, isMulticast)
|
||||
{
|
||||
}
|
||||
|
||||
// will either return a valid socket or will set exceptionToBeThrown
|
||||
protected UdpSocket GetSendSocket(IPAddress address, Uri destination, out Exception exceptionToBeThrown)
|
||||
{
|
||||
Fx.Assert(this.IsMulticast == false, "This overload should only be used for unicast.");
|
||||
|
||||
UdpSocket result = null;
|
||||
exceptionToBeThrown = null;
|
||||
AddressFamily family = address.AddressFamily;
|
||||
|
||||
lock (ThisLock)
|
||||
{
|
||||
if (this.State == CommunicationState.Opened)
|
||||
{
|
||||
for (int i = 0; i < this.SendSockets.Length; i++)
|
||||
{
|
||||
if (family == this.SendSockets[i].AddressFamily)
|
||||
{
|
||||
result = this.SendSockets[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
exceptionToBeThrown = new InvalidOperationException(SR.RemoteAddressUnreachableDueToIPVersionMismatch(destination));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
exceptionToBeThrown = CreateObjectDisposedException();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// will either return a valid socket or will set exceptionToBeThrown
|
||||
protected UdpSocket GetSendSocket(int interfaceIndex, out Exception exceptionToBeThrown)
|
||||
{
|
||||
Fx.Assert(this.IsMulticast == true, "This overload should only be used for multicast.");
|
||||
|
||||
UdpSocket result = null;
|
||||
exceptionToBeThrown = null;
|
||||
|
||||
lock (ThisLock)
|
||||
{
|
||||
if (this.State == CommunicationState.Opened)
|
||||
{
|
||||
for (int i = 0; i < this.SendSockets.Length; i++)
|
||||
{
|
||||
if (interfaceIndex == this.SendSockets[i].InterfaceIndex)
|
||||
{
|
||||
result = this.SendSockets[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
exceptionToBeThrown = new InvalidOperationException(SR.UdpSendFailedInterfaceIndexMatchNotFound(interfaceIndex));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
exceptionToBeThrown = CreateObjectDisposedException();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Must return non-null/non-empty array unless exceptionToBeThrown is has been set
|
||||
protected override UdpSocket[] GetSendSockets(Message message, out IPEndPoint remoteEndPoint, out Exception exceptionToBeThrown)
|
||||
{
|
||||
Fx.Assert(message != null, "message can't be null");
|
||||
|
||||
UdpSocket[] socketList = null;
|
||||
exceptionToBeThrown = null;
|
||||
|
||||
remoteEndPoint = null;
|
||||
Uri destination;
|
||||
bool isVia = false;
|
||||
|
||||
if (message.Properties.Via != null)
|
||||
{
|
||||
destination = message.Properties.Via;
|
||||
isVia = true;
|
||||
}
|
||||
else if (message.Headers.To != null)
|
||||
{
|
||||
destination = message.Headers.To;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.ToOrViaRequired));
|
||||
}
|
||||
|
||||
this.ValidateDestinationUri(destination, isVia);
|
||||
|
||||
if (destination.HostNameType == UriHostNameType.IPv4 || destination.HostNameType == UriHostNameType.IPv6)
|
||||
{
|
||||
remoteEndPoint = new IPEndPoint(IPAddress.Parse(destination.DnsSafeHost), destination.Port);
|
||||
|
||||
if (this.IsMulticast)
|
||||
{
|
||||
UdpSocket socket = this.GetSendSocketUsingInterfaceIndex(message.Properties, out exceptionToBeThrown);
|
||||
|
||||
if (socket != null)
|
||||
{
|
||||
if (socket.AddressFamily == remoteEndPoint.AddressFamily)
|
||||
{
|
||||
socketList = new UdpSocket[] { socket };
|
||||
}
|
||||
else
|
||||
{
|
||||
exceptionToBeThrown = new InvalidOperationException(SR.RemoteAddressUnreachableDueToIPVersionMismatch(destination.DnsSafeHost));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UdpSocket socket = this.GetSendSocket(remoteEndPoint.Address, destination, out exceptionToBeThrown);
|
||||
if (socket != null)
|
||||
{
|
||||
socketList = new UdpSocket[] { socket };
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IPAddress[] remoteAddresses = DnsCache.Resolve(destination).AddressList;
|
||||
|
||||
if (this.IsMulticast)
|
||||
{
|
||||
UdpSocket socket = this.GetSendSocketUsingInterfaceIndex(message.Properties, out exceptionToBeThrown);
|
||||
|
||||
if (socket != null)
|
||||
{
|
||||
socketList = new UdpSocket[] { socket };
|
||||
|
||||
for (int i = 0; i < remoteAddresses.Length; i++)
|
||||
{
|
||||
if (remoteAddresses[i].AddressFamily == socket.AddressFamily)
|
||||
{
|
||||
remoteEndPoint = new IPEndPoint(remoteAddresses[i], destination.Port);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (remoteEndPoint == null)
|
||||
{
|
||||
// for multicast, we only listen on either IPv4 or IPv6 (not both).
|
||||
// if we didn't find a matching remote endpoint, then it would indicate that
|
||||
// the remote host didn't resolve to an address we can use...
|
||||
exceptionToBeThrown = new InvalidOperationException(SR.RemoteAddressUnreachableDueToIPVersionMismatch(destination.DnsSafeHost));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool useIPv4 = true;
|
||||
bool useIPv6 = true;
|
||||
|
||||
for (int i = 0; i < remoteAddresses.Length; i++)
|
||||
{
|
||||
IPAddress address = remoteAddresses[i];
|
||||
|
||||
if (address.AddressFamily == AddressFamily.InterNetwork && useIPv4)
|
||||
{
|
||||
UdpSocket socket = this.GetSendSocket(address, destination, out exceptionToBeThrown);
|
||||
if (socket == null)
|
||||
{
|
||||
if (this.State != CommunicationState.Opened)
|
||||
{
|
||||
// time to exit, the channel is closing down.
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no matching socket on IPv4, so ignore future IPv4 addresses
|
||||
// in the remoteAddresses list
|
||||
useIPv4 = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
remoteEndPoint = new IPEndPoint(address, destination.Port);
|
||||
socketList = new UdpSocket[] { socket };
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (address.AddressFamily == AddressFamily.InterNetworkV6 && useIPv6)
|
||||
{
|
||||
UdpSocket socket = this.GetSendSocket(address, destination, out exceptionToBeThrown);
|
||||
if (socket == null)
|
||||
{
|
||||
if (this.State != CommunicationState.Opened)
|
||||
{
|
||||
// time to exit, the channel is closing down.
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no matching socket on IPv6, so ignore future IPv6 addresses
|
||||
// in the remoteAddresses list
|
||||
useIPv6 = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
remoteEndPoint = new IPEndPoint(address, destination.Port);
|
||||
socketList = new UdpSocket[] { socket };
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return socketList;
|
||||
}
|
||||
|
||||
private UdpSocket GetSendSocketUsingInterfaceIndex(MessageProperties properties, out Exception exceptionToBeThrown)
|
||||
{
|
||||
NetworkInterfaceMessageProperty property;
|
||||
UdpSocket socket = null;
|
||||
exceptionToBeThrown = null;
|
||||
|
||||
if (!NetworkInterfaceMessageProperty.TryGet(properties, out property))
|
||||
{
|
||||
if (this.SendSockets.Length > 1)
|
||||
{
|
||||
// this property is required on all messages sent from the channel listener.
|
||||
// the client channel does not use this method to get the send SendSockets or the
|
||||
// remote endpoint, so it is safe to throw...
|
||||
exceptionToBeThrown = new InvalidOperationException(SR.NetworkInterfaceMessagePropertyMissing(typeof(NetworkInterfaceMessageProperty)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// there is only one socket, so just send it on that one.
|
||||
socket = this.SendSockets[0];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
socket = this.GetSendSocket(property.InterfaceIndex, out exceptionToBeThrown);
|
||||
}
|
||||
|
||||
return socket;
|
||||
}
|
||||
|
||||
private void ValidateDestinationUri(Uri destination, bool isVia)
|
||||
{
|
||||
if (!destination.Scheme.Equals(UdpConstants.Scheme, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (isVia)
|
||||
{
|
||||
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.ViaUriIsNotValid(destination, SR.UriSchemeNotSupported(destination.Scheme))));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.ToAddressIsNotValid(destination, SR.UriSchemeNotSupported(destination.Scheme))));
|
||||
}
|
||||
}
|
||||
|
||||
if (destination.Port < 1 || destination.Port > IPEndPoint.MaxPort)
|
||||
{
|
||||
if (isVia)
|
||||
{
|
||||
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.ViaUriIsNotValid(destination, SR.PortNumberInvalid(1, IPEndPoint.MaxPort))));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.ToAddressIsNotValid(destination, SR.PortNumberInvalid(1, IPEndPoint.MaxPort))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
//----------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//----------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Channels
|
||||
{
|
||||
using System;
|
||||
|
||||
class SynchronizedRandom : Random
|
||||
{
|
||||
|
||||
public SynchronizedRandom()
|
||||
: base()
|
||||
{
|
||||
this.ThisLock = new object();
|
||||
}
|
||||
|
||||
public SynchronizedRandom(int seed)
|
||||
: base(seed)
|
||||
{
|
||||
this.ThisLock = new object();
|
||||
}
|
||||
|
||||
protected object ThisLock
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public override int Next(int minValue, int maxValue)
|
||||
{
|
||||
lock (this.ThisLock)
|
||||
{
|
||||
return base.Next(minValue, maxValue);
|
||||
}
|
||||
}
|
||||
|
||||
public override int Next()
|
||||
{
|
||||
lock (this.ThisLock)
|
||||
{
|
||||
return base.Next();
|
||||
}
|
||||
}
|
||||
|
||||
public override int Next(int maxValue)
|
||||
{
|
||||
lock (this.ThisLock)
|
||||
{
|
||||
return base.Next(maxValue);
|
||||
}
|
||||
}
|
||||
|
||||
public override void NextBytes(byte[] buffer)
|
||||
{
|
||||
lock (this.ThisLock)
|
||||
{
|
||||
base.NextBytes(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public override double NextDouble()
|
||||
{
|
||||
lock (this.ThisLock)
|
||||
{
|
||||
return base.NextDouble();
|
||||
}
|
||||
}
|
||||
|
||||
protected override double Sample()
|
||||
{
|
||||
lock (this.ThisLock)
|
||||
{
|
||||
return base.Sample();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,498 @@
|
||||
// <copyright>
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace System.ServiceModel.Channels
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime;
|
||||
using System.Runtime.Diagnostics;
|
||||
using System.ServiceModel.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Xml;
|
||||
|
||||
internal abstract class UdpChannelBase<QueueItemType> : InputQueueChannel<QueueItemType>, IUdpReceiveHandler
|
||||
where QueueItemType : class, IDisposable
|
||||
{
|
||||
private bool cleanedUp;
|
||||
private long pendingMessagesTotalSize;
|
||||
private long maxPendingMessagesTotalSize;
|
||||
private int maxReceivedMessageSize;
|
||||
private UdpRetransmissionSettings retransmitSettings;
|
||||
private Uri via;
|
||||
|
||||
protected UdpChannelBase(
|
||||
ChannelManagerBase channelManager,
|
||||
MessageEncoder encoder,
|
||||
BufferManager bufferManager,
|
||||
UdpSocket[] sockets,
|
||||
UdpRetransmissionSettings retransmissionSettings,
|
||||
long maxPendingMessagesTotalSize,
|
||||
EndpointAddress localAddress,
|
||||
Uri via,
|
||||
bool isMulticast,
|
||||
int maxReceivedMessageSize)
|
||||
: base(channelManager)
|
||||
{
|
||||
Fx.Assert(encoder != null, "encoder shouldn't be null");
|
||||
Fx.Assert(bufferManager != null, "buffer manager shouldn't be null");
|
||||
Fx.Assert(sockets != null, "sendSockets can't be null");
|
||||
Fx.Assert(sockets.Length > 0, "sendSockets can't be empty");
|
||||
Fx.Assert(retransmissionSettings != null, "retransmissionSettings can't be null");
|
||||
Fx.Assert(maxPendingMessagesTotalSize >= 0, "maxPendingMessagesTotalSize must be >= 0");
|
||||
Fx.Assert(maxReceivedMessageSize > 0, "maxReceivedMessageSize must be > 0");
|
||||
Fx.Assert(localAddress != null, "localAddress can't be null");
|
||||
Fx.Assert(via != null, "via can't be null");
|
||||
|
||||
this.maxPendingMessagesTotalSize = maxPendingMessagesTotalSize == UdpConstants.Defaults.DefaultMaxPendingMessagesTotalSize ? UdpConstants.Defaults.MaxPendingMessagesTotalSize : maxPendingMessagesTotalSize;
|
||||
this.Encoder = encoder;
|
||||
this.Sockets = sockets;
|
||||
this.BufferManager = bufferManager;
|
||||
this.retransmitSettings = retransmissionSettings;
|
||||
this.IsMulticast = isMulticast;
|
||||
this.DuplicateDetector = null;
|
||||
this.ReceiveManager = null;
|
||||
this.OwnsBufferManager = false;
|
||||
this.maxReceivedMessageSize = maxReceivedMessageSize;
|
||||
this.LocalAddress = localAddress;
|
||||
this.via = via;
|
||||
}
|
||||
|
||||
public EndpointAddress LocalAddress
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Uri Via
|
||||
{
|
||||
get { return this.via; }
|
||||
}
|
||||
|
||||
int IUdpReceiveHandler.MaxReceivedMessageSize
|
||||
{
|
||||
get { return this.maxReceivedMessageSize; }
|
||||
}
|
||||
|
||||
protected abstract bool IgnoreSerializationException { get; }
|
||||
|
||||
protected bool OwnsBufferManager { get; set; }
|
||||
|
||||
protected DuplicateMessageDetector DuplicateDetector { get; set; }
|
||||
|
||||
protected UdpSocketReceiveManager ReceiveManager { get; set; }
|
||||
|
||||
protected BufferManager BufferManager
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
protected MessageEncoder Encoder
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
protected bool IsMulticast
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
protected UdpOutputChannel UdpOutputChannel { get; private set; }
|
||||
|
||||
protected UdpSocket[] Sockets
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.StyleCop.CSharp.ReadabilityRules", "SA1100:DoNotPrefixCallsWithBaseUnlessLocalImplementationExists", Justification = "StyleCop 4.5 does not validate this rule properly.")]
|
||||
public override T GetProperty<T>()
|
||||
{
|
||||
if (typeof(T) == typeof(IDuplexChannel))
|
||||
{
|
||||
return (T)(object)this;
|
||||
}
|
||||
|
||||
T outputChannelProperty = this.UdpOutputChannel.GetProperty<T>();
|
||||
if (outputChannelProperty != null)
|
||||
{
|
||||
return outputChannelProperty;
|
||||
}
|
||||
|
||||
T messageEncoderProperty = this.Encoder.GetProperty<T>();
|
||||
if (messageEncoderProperty != null)
|
||||
{
|
||||
return messageEncoderProperty;
|
||||
}
|
||||
|
||||
return base.GetProperty<T>();
|
||||
}
|
||||
|
||||
// returns false if the message was dropped because the max pending message count was hit.
|
||||
bool IUdpReceiveHandler.HandleDataReceived(ArraySegment<byte> data, EndPoint remoteEndpoint, int interfaceIndex, Action onMessageDequeuedCallback)
|
||||
{
|
||||
bool returnBuffer = true;
|
||||
string messageHash = null;
|
||||
Message message = null;
|
||||
bool continueReceiving = true;
|
||||
|
||||
try
|
||||
{
|
||||
IPEndPoint remoteIPEndPoint = (IPEndPoint)remoteEndpoint;
|
||||
|
||||
message = UdpUtility.DecodeMessage(
|
||||
this.DuplicateDetector,
|
||||
this.Encoder,
|
||||
this.BufferManager,
|
||||
data,
|
||||
remoteIPEndPoint,
|
||||
interfaceIndex,
|
||||
this.IgnoreSerializationException,
|
||||
out messageHash);
|
||||
|
||||
if (message != null)
|
||||
{
|
||||
// We pass in the length of the message buffer instead of the length of the message to keep track of the amount of memory that's been allocated
|
||||
continueReceiving = this.EnqueueMessage(message, data.Array.Length, onMessageDequeuedCallback);
|
||||
returnBuffer = !continueReceiving;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (Fx.IsFatal(e))
|
||||
{
|
||||
returnBuffer = false;
|
||||
throw;
|
||||
}
|
||||
|
||||
this.HandleReceiveException(e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (returnBuffer)
|
||||
{
|
||||
if (message != null)
|
||||
{
|
||||
if (this.DuplicateDetector != null)
|
||||
{
|
||||
Fx.Assert(messageHash != null, "message hash should always be available if duplicate detector is enabled");
|
||||
this.DuplicateDetector.RemoveEntry(messageHash);
|
||||
}
|
||||
|
||||
message.Close(); // implicitly returns the buffer
|
||||
}
|
||||
else
|
||||
{
|
||||
this.BufferManager.ReturnBuffer(data.Array);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return continueReceiving;
|
||||
}
|
||||
|
||||
void IUdpReceiveHandler.HandleAsyncException(Exception ex)
|
||||
{
|
||||
this.HandleReceiveException(ex);
|
||||
}
|
||||
|
||||
internal virtual void HandleReceiveException(Exception ex)
|
||||
{
|
||||
this.EnqueueAndDispatch(UdpUtility.WrapAsyncException(ex), null, false);
|
||||
}
|
||||
|
||||
// Since ChannelListener and channel lifetimes can be different, we need a
|
||||
// way to transfer the socketReceiveManager and DuplicateMessageDetection
|
||||
// objects to the channel if the listener gets closed. If this method succeeds, then
|
||||
// this also indicates that the bufferManager is no longer owned by the channel listener,
|
||||
// so we have to clean that up also.
|
||||
internal bool TransferReceiveManagerOwnership(UdpSocketReceiveManager socketReceiveManager, DuplicateMessageDetector duplicateDetector)
|
||||
{
|
||||
bool success = false;
|
||||
if (this.State == CommunicationState.Opened)
|
||||
{
|
||||
lock (ThisLock)
|
||||
{
|
||||
if (this.State == CommunicationState.Opened)
|
||||
{
|
||||
Fx.Assert(this.ReceiveManager == null, "ReceiveManager is already set to a non-null value");
|
||||
Fx.Assert(this.DuplicateDetector == null, "DuplicateDetector is already set to a non-null value");
|
||||
|
||||
this.ReceiveManager = socketReceiveManager;
|
||||
this.OwnsBufferManager = true;
|
||||
this.ReceiveManager.SetReceiveHandler(this);
|
||||
this.DuplicateDetector = duplicateDetector;
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
// returns false if the max pending messages total size was hit.
|
||||
internal bool EnqueueMessage(Message message, int messageBufferSize, Action messageDequeuedCallback)
|
||||
{
|
||||
Action onMessageDequeuedCallback = () =>
|
||||
{
|
||||
lock (this.ThisLock)
|
||||
{
|
||||
this.pendingMessagesTotalSize -= messageBufferSize;
|
||||
Fx.Assert(this.pendingMessagesTotalSize >= 0, "pendingMessagesTotalSize should not be negative.");
|
||||
}
|
||||
|
||||
messageDequeuedCallback();
|
||||
};
|
||||
|
||||
bool success = false;
|
||||
lock (this.ThisLock)
|
||||
{
|
||||
if (this.pendingMessagesTotalSize + messageBufferSize <= this.maxPendingMessagesTotalSize)
|
||||
{
|
||||
message.Properties.Via = this.Via;
|
||||
this.pendingMessagesTotalSize += messageBufferSize;
|
||||
try
|
||||
{
|
||||
this.FinishEnqueueMessage(message, onMessageDequeuedCallback, false);
|
||||
success = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!success)
|
||||
{
|
||||
this.pendingMessagesTotalSize -= messageBufferSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TD.MaxPendingMessagesTotalSizeReachedIsEnabled())
|
||||
{
|
||||
string messageIdString = string.Empty;
|
||||
if (message.Headers.MessageId != null)
|
||||
{
|
||||
messageIdString = string.Format(CultureInfo.CurrentCulture, "'{0}' ", message.Headers.MessageId.ToString());
|
||||
}
|
||||
|
||||
EventTraceActivity eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message);
|
||||
TD.MaxPendingMessagesTotalSizeReached(eventTraceActivity, messageIdString, this.maxPendingMessagesTotalSize, typeof(TransportBindingElement).FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
internal abstract void FinishEnqueueMessage(Message message, Action dequeuedCallback, bool canDispatchOnThisThread);
|
||||
|
||||
protected virtual void AddHeadersTo(Message message)
|
||||
{
|
||||
Fx.Assert(message != null, "Message can't be null");
|
||||
|
||||
if (message.Version.Addressing != AddressingVersion.None)
|
||||
{
|
||||
if (message.Headers.MessageId == null)
|
||||
{
|
||||
message.Headers.MessageId = new UniqueId();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.retransmitSettings.Enabled == true)
|
||||
{
|
||||
// we should only get here if some channel above us starts producing messages that don't match the encoder's message version.
|
||||
throw FxTrace.Exception.AsError(new ProtocolException(SR.RetransmissionRequiresAddressingOnMessage(message.Version.Addressing.ToString())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Closes the channel ungracefully during error conditions.
|
||||
protected override void OnAbort()
|
||||
{
|
||||
this.Cleanup(true, TimeSpan.Zero);
|
||||
}
|
||||
|
||||
protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
|
||||
{
|
||||
this.OnOpen(timeout);
|
||||
return new CompletedAsyncResult(callback, state);
|
||||
}
|
||||
|
||||
protected override void OnEndOpen(IAsyncResult result)
|
||||
{
|
||||
CompletedAsyncResult.End(result);
|
||||
}
|
||||
|
||||
protected override void OnOpen(TimeSpan timeout)
|
||||
{
|
||||
this.UdpOutputChannel.Open();
|
||||
}
|
||||
|
||||
protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
|
||||
{
|
||||
return new CloseAsyncResult<QueueItemType>(
|
||||
this,
|
||||
new ChainedBeginHandler(base.OnBeginClose),
|
||||
new ChainedEndHandler(base.OnEndClose),
|
||||
timeout,
|
||||
callback,
|
||||
state);
|
||||
}
|
||||
|
||||
protected override void OnEndClose(IAsyncResult result)
|
||||
{
|
||||
CloseAsyncResult<QueueItemType>.End(result);
|
||||
}
|
||||
|
||||
// Closes the channel gracefully during normal conditions.
|
||||
protected override void OnClose(TimeSpan timeout)
|
||||
{
|
||||
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
||||
this.Cleanup(false, timeoutHelper.RemainingTime());
|
||||
base.OnClose(timeoutHelper.RemainingTime());
|
||||
}
|
||||
|
||||
protected void SetOutputChannel(UdpOutputChannel udpOutputChannel)
|
||||
{
|
||||
Fx.Assert(this.UdpOutputChannel == null, "this.UdpOutputChannel must be null");
|
||||
Fx.Assert(udpOutputChannel != null, "udpOutputChannel can't be null, since SetOutputChannel should be called only once");
|
||||
|
||||
this.UdpOutputChannel = udpOutputChannel;
|
||||
}
|
||||
|
||||
// We're guaranteed by CommunicationObject that at most ONE of Close or BeginClose will be called once.
|
||||
protected void Cleanup(bool aborting, TimeSpan timeout)
|
||||
{
|
||||
if (this.cleanedUp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (ThisLock)
|
||||
{
|
||||
if (this.cleanedUp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (aborting)
|
||||
{
|
||||
this.UdpOutputChannel.Abort();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.UdpOutputChannel.Close(timeout);
|
||||
}
|
||||
|
||||
if (this.DuplicateDetector != null)
|
||||
{
|
||||
this.DuplicateDetector.Dispose();
|
||||
}
|
||||
|
||||
if (this.ReceiveManager != null)
|
||||
{
|
||||
this.ReceiveManager.Close();
|
||||
}
|
||||
|
||||
this.CleanupBufferManager();
|
||||
|
||||
this.cleanedUp = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void CleanupBufferManager()
|
||||
{
|
||||
if (this.OwnsBufferManager)
|
||||
{
|
||||
this.BufferManager.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Control flow for async path
|
||||
// We use this mechanism to avoid initializing two async objects as logically cleanup+close is one operation.
|
||||
// At any point in the Begin* methods, we may go async. The steps are:
|
||||
// - Close inner UdpOutputChannel
|
||||
// - Cleanup channel
|
||||
// - Close channel
|
||||
private class CloseAsyncResult<T> : AsyncResult
|
||||
where T : class, IDisposable
|
||||
{
|
||||
private static AsyncCompletion completeCloseOutputChannelCallback = new AsyncCompletion(CompleteCloseOutputChannel);
|
||||
private static AsyncCompletion completeBaseCloseCallback = new AsyncCompletion(CompleteBaseClose);
|
||||
|
||||
private UdpChannelBase<T> channel;
|
||||
private TimeoutHelper timeoutHelper;
|
||||
private ChainedBeginHandler baseBeginClose;
|
||||
private ChainedEndHandler baseEndClose;
|
||||
|
||||
public CloseAsyncResult(UdpChannelBase<T> channel, ChainedBeginHandler baseBeginClose, ChainedEndHandler baseEndClose, TimeSpan timeout, AsyncCallback callback, object state)
|
||||
: base(callback, state)
|
||||
{
|
||||
this.channel = channel;
|
||||
this.baseBeginClose = baseBeginClose;
|
||||
this.baseEndClose = baseEndClose;
|
||||
this.timeoutHelper = new TimeoutHelper(timeout);
|
||||
|
||||
if (this.BeginCloseOutputChannel())
|
||||
{
|
||||
this.Complete(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void End(IAsyncResult result)
|
||||
{
|
||||
AsyncResult.End<CloseAsyncResult<T>>(result);
|
||||
}
|
||||
|
||||
private static bool CompleteBaseClose(IAsyncResult result)
|
||||
{
|
||||
// AsyncResult.AsyncCompletionWrapperCallback takes care of catching exceptions for us.
|
||||
CloseAsyncResult<T> thisPtr = (CloseAsyncResult<T>)result.AsyncState;
|
||||
|
||||
// We are completing the base class close operation at this point.
|
||||
thisPtr.baseEndClose(result);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool CompleteCloseOutputChannel(IAsyncResult result)
|
||||
{
|
||||
// AsyncResult.AsyncCompletionWrapperCallback takes care of catching exceptions for us.
|
||||
CloseAsyncResult<T> thisPtr = (CloseAsyncResult<T>)result.AsyncState;
|
||||
|
||||
// We are completing the base class close operation at this point.
|
||||
thisPtr.channel.UdpOutputChannel.EndClose(result);
|
||||
|
||||
thisPtr.channel.Cleanup(false, thisPtr.timeoutHelper.RemainingTime());
|
||||
|
||||
return thisPtr.BeginBaseClose();
|
||||
}
|
||||
|
||||
private bool BeginCloseOutputChannel()
|
||||
{
|
||||
// AsyncResult.AsyncCompletionWrapperCallback takes care of catching the exceptions for us.
|
||||
IAsyncResult result = this.channel.UdpOutputChannel.BeginClose(this.timeoutHelper.RemainingTime(), this.PrepareAsyncCompletion(completeCloseOutputChannelCallback), this);
|
||||
|
||||
// SyncContinue calls CompleteCloseOutputChannel for us in [....] case.
|
||||
return this.SyncContinue(result);
|
||||
}
|
||||
|
||||
private bool BeginBaseClose()
|
||||
{
|
||||
// AsyncResult.AsyncCompletionWrapperCallback takes care of catching the exceptions for us.
|
||||
IAsyncResult result = this.baseBeginClose(this.timeoutHelper.RemainingTime(), this.PrepareAsyncCompletion(completeBaseCloseCallback), this);
|
||||
|
||||
// SyncContinue calls CompleteBaseClose for us in [....] case.
|
||||
return this.SyncContinue(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,385 @@
|
||||
//----------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//----------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Channels
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime;
|
||||
|
||||
class UdpChannelFactory<TChannel> : ChannelFactoryBase<TChannel>
|
||||
{
|
||||
MessageEncoderFactory messageEncoderFactory;
|
||||
UdpTransportBindingElement udpTransportBindingElement;
|
||||
|
||||
internal UdpChannelFactory(UdpTransportBindingElement transportBindingElement, BindingContext context)
|
||||
: base(context.Binding)
|
||||
{
|
||||
Fx.Assert(transportBindingElement != null, "transportBindingElement can't be null");
|
||||
Fx.Assert(context != null, "binding context can't be null");
|
||||
Fx.Assert(typeof(TChannel) == typeof(IOutputChannel) || typeof(TChannel) == typeof(IDuplexChannel), "this channel factory only supports IOutputChannel and IDuplexChannel");
|
||||
|
||||
this.udpTransportBindingElement = transportBindingElement;
|
||||
|
||||
// We should only throw this exception if the user specified realistic MaxReceivedMessageSize less than or equal to max message size over UDP.
|
||||
// If the user specified something bigger like Long.MaxValue, we shouldn't stop them.
|
||||
if (this.udpTransportBindingElement.MaxReceivedMessageSize <= UdpConstants.MaxMessageSizeOverIPv4 &&
|
||||
this.udpTransportBindingElement.SocketReceiveBufferSize < this.udpTransportBindingElement.MaxReceivedMessageSize)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentOutOfRange("SocketReceiveBufferSize", this.udpTransportBindingElement.SocketReceiveBufferSize,
|
||||
SR.Property1LessThanOrEqualToProperty2("MaxReceivedMessageSize", this.udpTransportBindingElement.MaxReceivedMessageSize,
|
||||
"SocketReceiveBufferSize", this.udpTransportBindingElement.SocketReceiveBufferSize));
|
||||
}
|
||||
|
||||
this.messageEncoderFactory = UdpUtility.GetEncoder(context);
|
||||
|
||||
bool retransmissionEnabled = this.udpTransportBindingElement.RetransmissionSettings.Enabled;
|
||||
//duplicated detection doesn't apply to IOutputChannel, so don't throw if we are only sending
|
||||
bool duplicateDetectionEnabled = this.udpTransportBindingElement.DuplicateMessageHistoryLength > 0 ? typeof(TChannel) != typeof(IOutputChannel) : false;
|
||||
UdpUtility.ValidateDuplicateDetectionAndRetransmittionSupport(this.messageEncoderFactory, retransmissionEnabled, duplicateDetectionEnabled);
|
||||
|
||||
int maxBufferSize = (int)Math.Min(transportBindingElement.MaxReceivedMessageSize, UdpConstants.MaxMessageSizeOverIPv4);
|
||||
this.BufferManager = BufferManager.CreateBufferManager(transportBindingElement.MaxBufferPoolSize, maxBufferSize);
|
||||
|
||||
}
|
||||
|
||||
BufferManager BufferManager
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public override T GetProperty<T>()
|
||||
{
|
||||
T messageEncoderProperty = this.messageEncoderFactory.Encoder.GetProperty<T>();
|
||||
if (messageEncoderProperty != null)
|
||||
{
|
||||
return messageEncoderProperty;
|
||||
}
|
||||
|
||||
if (typeof(T) == typeof(MessageVersion))
|
||||
{
|
||||
return (T)(object)this.messageEncoderFactory.Encoder.MessageVersion;
|
||||
}
|
||||
|
||||
return base.GetProperty<T>();
|
||||
}
|
||||
|
||||
|
||||
protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
|
||||
{
|
||||
this.OnOpen(timeout);
|
||||
return new CompletedAsyncResult(callback, state);
|
||||
}
|
||||
|
||||
protected override TChannel OnCreateChannel(EndpointAddress to, Uri via)
|
||||
{
|
||||
Fx.Assert(to != null, "To address should have been validated as non-null by ChannelFactoryBase");
|
||||
Fx.Assert(via != null, "Via address should have been validated as non-null by ChannelFactoryBase");
|
||||
|
||||
if (!via.IsAbsoluteUri)
|
||||
{
|
||||
throw FxTrace.Exception.Argument("via", SR.RelativeUriNotAllowed(via));
|
||||
}
|
||||
|
||||
if (!via.Scheme.Equals(UdpConstants.Scheme, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw FxTrace.Exception.Argument("via", SR.UriSchemeNotSupported(via.Scheme));
|
||||
}
|
||||
|
||||
if (!UdpUtility.IsSupportedHostNameType(via.HostNameType))
|
||||
{
|
||||
throw FxTrace.Exception.Argument("via", SR.UnsupportedUriHostNameType(via.Host, via.HostNameType));
|
||||
}
|
||||
|
||||
if (via.IsDefaultPort || via.Port == 0)
|
||||
{
|
||||
throw FxTrace.Exception.ArgumentOutOfRange("via", via, SR.PortNumberRequiredOnVia(via));
|
||||
}
|
||||
|
||||
UdpSocket[] sockets = null;
|
||||
IPEndPoint remoteEndPoint = null;
|
||||
TChannel channel;
|
||||
|
||||
lock (this.ThisLock)
|
||||
{
|
||||
bool isMulticast;
|
||||
sockets = GetSockets(via, out remoteEndPoint, out isMulticast);
|
||||
|
||||
EndpointAddress localAddress = new EndpointAddress(EndpointAddress.AnonymousUri);
|
||||
|
||||
if (typeof(TChannel) == typeof(IDuplexChannel))
|
||||
{
|
||||
UdpChannelFactory<IDuplexChannel> duplexChannelFactory = (UdpChannelFactory<IDuplexChannel>)(object)this;
|
||||
channel = (TChannel)(object)new ClientUdpDuplexChannel(duplexChannelFactory, sockets, remoteEndPoint, localAddress, to, via, isMulticast);
|
||||
}
|
||||
else
|
||||
{
|
||||
UdpChannelFactory<IOutputChannel> outputChannelFactory = (UdpChannelFactory<IOutputChannel>)(object)this;
|
||||
channel = (TChannel)(object)new ClientUdpOutputChannel(
|
||||
outputChannelFactory,
|
||||
remoteEndPoint,
|
||||
outputChannelFactory.messageEncoderFactory.Encoder,
|
||||
this.BufferManager,
|
||||
sockets,
|
||||
outputChannelFactory.udpTransportBindingElement.RetransmissionSettings,
|
||||
to,
|
||||
via,
|
||||
isMulticast);
|
||||
}
|
||||
}
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
protected override void OnEndOpen(IAsyncResult result)
|
||||
{
|
||||
CompletedAsyncResult.End(result);
|
||||
}
|
||||
|
||||
protected override void OnOpen(TimeSpan timeout)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//will only return > 1 socket when both of the following are true:
|
||||
// 1) multicast
|
||||
// 2) sending on all interfaces
|
||||
UdpSocket[] GetSockets(Uri via, out IPEndPoint remoteEndPoint, out bool isMulticast)
|
||||
{
|
||||
UdpSocket[] results = null;
|
||||
|
||||
remoteEndPoint = null;
|
||||
IPAddress[] remoteAddressList;
|
||||
isMulticast = false;
|
||||
|
||||
UdpUtility.ThrowIfNoSocketSupport();
|
||||
|
||||
if (via.HostNameType == UriHostNameType.IPv6 || via.HostNameType == UriHostNameType.IPv4)
|
||||
{
|
||||
UdpUtility.ThrowOnUnsupportedHostNameType(via);
|
||||
|
||||
IPAddress address = IPAddress.Parse(via.DnsSafeHost);
|
||||
isMulticast = UdpUtility.IsMulticastAddress(address);
|
||||
|
||||
remoteAddressList = new IPAddress[] { address };
|
||||
}
|
||||
else
|
||||
{
|
||||
remoteAddressList = DnsCache.Resolve(via).AddressList;
|
||||
}
|
||||
|
||||
if (remoteAddressList.Length < 1)
|
||||
{
|
||||
// System.Net.Dns shouldn't ever allow this to happen, but...
|
||||
Fx.Assert("DnsCache returned a HostEntry with zero length address list");
|
||||
throw FxTrace.Exception.AsError(new EndpointNotFoundException(SR.DnsResolveFailed(via.DnsSafeHost)));
|
||||
}
|
||||
|
||||
remoteEndPoint = new IPEndPoint(remoteAddressList[0], via.Port);
|
||||
|
||||
IPAddress localAddress;
|
||||
if (via.IsLoopback)
|
||||
{
|
||||
localAddress = (remoteEndPoint.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Loopback : IPAddress.IPv6Loopback);
|
||||
}
|
||||
else
|
||||
{
|
||||
localAddress = (remoteEndPoint.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any);
|
||||
}
|
||||
|
||||
int port = 0;
|
||||
|
||||
if (isMulticast)
|
||||
{
|
||||
List<UdpSocket> socketList = new List<UdpSocket>();
|
||||
NetworkInterface[] adapters = UdpUtility.GetMulticastInterfaces(this.udpTransportBindingElement.MulticastInterfaceId);
|
||||
|
||||
//if listening on a specific adapter, don't disable multicast loopback on that adapter.
|
||||
bool allowMulticastLoopback = !string.IsNullOrEmpty(this.udpTransportBindingElement.MulticastInterfaceId);
|
||||
|
||||
for (int i = 0; i < adapters.Length; i++)
|
||||
{
|
||||
if (adapters[i].OperationalStatus == OperationalStatus.Up)
|
||||
{
|
||||
IPInterfaceProperties properties = adapters[i].GetIPProperties();
|
||||
bool isLoopbackAdapter = adapters[i].NetworkInterfaceType == NetworkInterfaceType.Loopback;
|
||||
|
||||
if (isLoopbackAdapter)
|
||||
{
|
||||
int interfaceIndex;
|
||||
if (UdpUtility.TryGetLoopbackInterfaceIndex(adapters[i], localAddress.AddressFamily == AddressFamily.InterNetwork, out interfaceIndex))
|
||||
{
|
||||
socketList.Add(UdpUtility.CreateListenSocket(localAddress, ref port, this.udpTransportBindingElement.SocketReceiveBufferSize, this.udpTransportBindingElement.TimeToLive,
|
||||
interfaceIndex, allowMulticastLoopback, isLoopbackAdapter));
|
||||
}
|
||||
|
||||
}
|
||||
else if (localAddress.AddressFamily == AddressFamily.InterNetworkV6)
|
||||
{
|
||||
if (adapters[i].Supports(NetworkInterfaceComponent.IPv6))
|
||||
{
|
||||
IPv6InterfaceProperties v6Properties = properties.GetIPv6Properties();
|
||||
|
||||
if (v6Properties != null)
|
||||
{
|
||||
socketList.Add(UdpUtility.CreateListenSocket(localAddress, ref port, this.udpTransportBindingElement.SocketReceiveBufferSize,
|
||||
this.udpTransportBindingElement.TimeToLive, v6Properties.Index, allowMulticastLoopback, isLoopbackAdapter));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (adapters[i].Supports(NetworkInterfaceComponent.IPv4))
|
||||
{
|
||||
IPv4InterfaceProperties v4Properties = properties.GetIPv4Properties();
|
||||
if (v4Properties != null)
|
||||
{
|
||||
socketList.Add(UdpUtility.CreateListenSocket(localAddress, ref port, this.udpTransportBindingElement.SocketReceiveBufferSize,
|
||||
this.udpTransportBindingElement.TimeToLive, v4Properties.Index, allowMulticastLoopback, isLoopbackAdapter));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//CreateListenSocket sets the port, but since we aren't listening
|
||||
//on multicast, each socket can't share the same port.
|
||||
port = 0;
|
||||
}
|
||||
|
||||
if (socketList.Count == 0)
|
||||
{
|
||||
throw FxTrace.Exception.AsError(new ArgumentException(SR.UdpFailedToFindMulticastAdapter(via)));
|
||||
}
|
||||
|
||||
results = socketList.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
UdpSocket socket = UdpUtility.CreateUnicastListenSocket(localAddress, ref port, this.udpTransportBindingElement.SocketReceiveBufferSize,
|
||||
this.udpTransportBindingElement.TimeToLive);
|
||||
|
||||
results = new UdpSocket[] { socket };
|
||||
}
|
||||
|
||||
|
||||
Fx.Assert(results != null, "GetSockets(...) return results should never be null. An exception should have been thrown but wasn't.");
|
||||
return results;
|
||||
|
||||
}
|
||||
|
||||
sealed class ClientUdpDuplexChannel : UdpDuplexChannel
|
||||
{
|
||||
EndpointAddress to;
|
||||
ChannelParameterCollection channelParameters;
|
||||
|
||||
internal ClientUdpDuplexChannel(UdpChannelFactory<IDuplexChannel> factory, UdpSocket[] sockets, IPEndPoint remoteEndPoint, EndpointAddress localAddress, EndpointAddress to, Uri via, bool isMulticast)
|
||||
: base(factory,
|
||||
factory.messageEncoderFactory.Encoder,
|
||||
factory.BufferManager,
|
||||
sockets,
|
||||
factory.udpTransportBindingElement.RetransmissionSettings,
|
||||
factory.udpTransportBindingElement.MaxPendingMessagesTotalSize,
|
||||
localAddress,
|
||||
via,
|
||||
isMulticast,
|
||||
(int)factory.udpTransportBindingElement.MaxReceivedMessageSize)
|
||||
{
|
||||
Fx.Assert(to != null, "to address can't be null for this constructor...");
|
||||
Fx.Assert(remoteEndPoint != null, "remoteEndPoint can't be null");
|
||||
|
||||
this.RemoteEndPoint = remoteEndPoint;
|
||||
this.to = to;
|
||||
|
||||
if (factory.udpTransportBindingElement.DuplicateMessageHistoryLength > 0)
|
||||
{
|
||||
this.DuplicateDetector = new DuplicateMessageDetector(factory.udpTransportBindingElement.DuplicateMessageHistoryLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.DuplicateDetector = null;
|
||||
}
|
||||
|
||||
UdpOutputChannel udpOutputChannel = new ClientUdpOutputChannel(factory, remoteEndPoint, factory.messageEncoderFactory.Encoder, factory.BufferManager, sockets, factory.udpTransportBindingElement.RetransmissionSettings, to, via, isMulticast);
|
||||
this.SetOutputChannel(udpOutputChannel);
|
||||
}
|
||||
|
||||
protected override bool IgnoreSerializationException
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.IsMulticast;
|
||||
}
|
||||
}
|
||||
|
||||
public override EndpointAddress RemoteAddress
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.to;
|
||||
}
|
||||
}
|
||||
|
||||
public IPEndPoint RemoteEndPoint
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public override T GetProperty<T>()
|
||||
{
|
||||
if (typeof(T) == typeof(ChannelParameterCollection))
|
||||
{
|
||||
if (this.State == CommunicationState.Created)
|
||||
{
|
||||
lock (ThisLock)
|
||||
{
|
||||
if (this.channelParameters == null)
|
||||
{
|
||||
this.channelParameters = new ChannelParameterCollection();
|
||||
}
|
||||
}
|
||||
}
|
||||
return (T)(object)this.channelParameters;
|
||||
}
|
||||
else
|
||||
{
|
||||
return base.GetProperty<T>();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnOpened()
|
||||
{
|
||||
this.ReceiveManager = new UdpSocketReceiveManager(this.Sockets,
|
||||
UdpConstants.PendingReceiveCountPerProcessor * Environment.ProcessorCount,
|
||||
base.BufferManager,
|
||||
this);
|
||||
|
||||
//do the state change to CommunicationState.Opened before starting the receive loop.
|
||||
//this avoids a ---- between transitioning state and processing messages that are
|
||||
//already in the socket receive buffer.
|
||||
base.OnOpened();
|
||||
|
||||
this.ReceiveManager.Open();
|
||||
}
|
||||
|
||||
protected override void AddHeadersTo(Message message)
|
||||
{
|
||||
Fx.Assert(message != null, "Message can't be null");
|
||||
|
||||
if (message.Version.Addressing != AddressingVersion.None)
|
||||
{
|
||||
this.to.ApplyTo(message);
|
||||
}
|
||||
|
||||
message.Properties.Via = this.Via;
|
||||
|
||||
base.AddHeadersTo(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,53 @@
|
||||
//----------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//----------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Channels
|
||||
{
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
static class UdpConstants
|
||||
{
|
||||
// max is 64K - 20 (IP header) - 8(UDP header) - 1 (wraparound)
|
||||
public const int MaxMessageSizeOverIPv4 = 64 * 1024 - 20 - 8 - 1;
|
||||
public const int MaxTimeToLive = 255;
|
||||
public const long MinPendingMessagesTotalSize = 0;
|
||||
public const int MinReceiveBufferSize = 1;
|
||||
public const int MinTimeToLive = 0; //localhost traffic only
|
||||
public const int PendingReceiveCountPerProcessor = 2;
|
||||
public const string Scheme = "soap.udp";
|
||||
public const string TimeSpanZero = "00:00:00";
|
||||
public const string WsdlSoapUdpTransportUri = "http://schemas.microsoft.com/soap/udp";
|
||||
public const string WsdlSoapUdpTransportNamespace = "http://schemas.microsoft.com/ws/06/2010/policy/soap/udp";
|
||||
public const string WsdlSoapUdpTransportPrefix = "sud";
|
||||
public const string RetransmissionEnabled = "RetransmissionEnabled";
|
||||
|
||||
internal static class Defaults
|
||||
{
|
||||
public static readonly TimeSpan ReceiveTimeout = TimeSpan.FromMinutes(1);
|
||||
public static readonly TimeSpan SendTimeout = TimeSpan.FromMinutes(1);
|
||||
public const string EncodingString = "utf-8";
|
||||
public const string DelayLowerBound = "00:00:00.050";
|
||||
public const string DelayUpperBound = "00:00:00.250";
|
||||
public const int DuplicateMessageHistoryLength = 0;
|
||||
public const int DuplicateMessageHistoryLengthWithRetransmission = 4096;
|
||||
public const int InterfaceIndex = -1;
|
||||
public const string MaxDelayPerRetransmission = "00:00:00.500";
|
||||
public const int MaxRetransmitCount = 0;
|
||||
public const int MaxUnicastRetransmitCount = 0;
|
||||
public const int MaxMulticastRetransmitCount = 0;
|
||||
public const long DefaultMaxPendingMessagesTotalSize = 0;
|
||||
public static readonly long MaxPendingMessagesTotalSize = 1024 * 1024 * Environment.ProcessorCount; // 512 * 2K messages per processor
|
||||
public const long MaxReceivedMessageSize = SocketReceiveBufferSize;
|
||||
public const string MulticastInterfaceId = null;
|
||||
public const int SocketReceiveBufferSize = 64 * 1024;
|
||||
public const int TimeToLive = 1;
|
||||
public static MessageEncoderFactory MessageEncoderFactory = new TextMessageEncodingBindingElement().CreateMessageEncoderFactory();
|
||||
|
||||
public static readonly TimeSpan DelayLowerBoundTimeSpan = TimeSpan.Parse(DelayLowerBound, CultureInfo.InvariantCulture);
|
||||
public static readonly TimeSpan DelayUpperBoundTimeSpan = TimeSpan.Parse(DelayUpperBound, CultureInfo.InvariantCulture);
|
||||
public static readonly TimeSpan MaxDelayPerRetransmissionTimeSpan = TimeSpan.Parse(MaxDelayPerRetransmission, CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,197 @@
|
||||
//----------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//----------------------------------------------------------------
|
||||
|
||||
namespace System.ServiceModel.Channels
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime;
|
||||
using System.Runtime.Diagnostics;
|
||||
using System.ServiceModel.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Xml;
|
||||
|
||||
abstract class UdpDuplexChannel : UdpChannelBase<Message>, IDuplexChannel
|
||||
{
|
||||
protected UdpDuplexChannel(
|
||||
ChannelManagerBase channelMananger,
|
||||
MessageEncoder encoder,
|
||||
BufferManager bufferManager,
|
||||
UdpSocket[] sendSockets,
|
||||
UdpRetransmissionSettings retransmissionSettings,
|
||||
long maxPendingMessagesTotalSize,
|
||||
EndpointAddress localAddress,
|
||||
Uri via,
|
||||
bool isMulticast,
|
||||
int maxReceivedMessageSize)
|
||||
: base(channelMananger, encoder, bufferManager, sendSockets, retransmissionSettings, maxPendingMessagesTotalSize, localAddress, via, isMulticast, maxReceivedMessageSize)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual EndpointAddress RemoteAddress
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public override T GetProperty<T>()
|
||||
{
|
||||
if (typeof(T) == typeof(IDuplexChannel))
|
||||
{
|
||||
return (T)(object)this;
|
||||
}
|
||||
|
||||
return base.GetProperty<T>();
|
||||
}
|
||||
|
||||
public IAsyncResult BeginSend(Message message, AsyncCallback callback, object state)
|
||||
{
|
||||
return this.BeginSend(message, this.DefaultSendTimeout, callback, state);
|
||||
}
|
||||
|
||||
public IAsyncResult BeginSend(Message message, TimeSpan timeout, AsyncCallback callback, object state)
|
||||
{
|
||||
ThrowIfDisposedOrNotOpen();
|
||||
|
||||
if (message is NullMessage)
|
||||
{
|
||||
return new CompletedAsyncResult(callback, state);
|
||||
}
|
||||
AddHeadersTo(message);
|
||||
return this.UdpOutputChannel.BeginSend(message, timeout, callback, state);
|
||||
}
|
||||
|
||||
public void EndSend(IAsyncResult result)
|
||||
{
|
||||
if (result is CompletedAsyncResult)
|
||||
{
|
||||
CompletedAsyncResult.End(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.UdpOutputChannel.EndSend(result);
|
||||
}
|
||||
}
|
||||
|
||||
public void Send(Message message)
|
||||
{
|
||||
this.Send(message, this.DefaultSendTimeout);
|
||||
}
|
||||
|
||||
public void Send(Message message, TimeSpan timeout)
|
||||
{
|
||||
if (message is NullMessage)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.UdpOutputChannel.Send(message, timeout);
|
||||
}
|
||||
|
||||
public Message Receive()
|
||||
{
|
||||
return this.Receive(this.DefaultReceiveTimeout);
|
||||
}
|
||||
|
||||
public Message Receive(TimeSpan timeout)
|
||||
{
|
||||
if (timeout < TimeSpan.Zero)
|
||||
{
|
||||
throw FxTrace.Exception.AsError(new ArgumentOutOfRangeException("timeout", timeout, SR.TimeoutOutOfRange0));
|
||||
}
|
||||
|
||||
this.ThrowPending();
|
||||
return InputChannel.HelpReceive(this, timeout);
|
||||
}
|
||||
|
||||
public IAsyncResult BeginReceive(AsyncCallback callback, object state)
|
||||
{
|
||||
return this.BeginReceive(this.DefaultReceiveTimeout, callback, state);
|
||||
}
|
||||
|
||||
public IAsyncResult BeginReceive(TimeSpan timeout, AsyncCallback callback, object state)
|
||||
{
|
||||
if (timeout < TimeSpan.Zero)
|
||||
{
|
||||
throw FxTrace.Exception.AsError(new ArgumentOutOfRangeException("timeout", timeout, SR.TimeoutOutOfRange0));
|
||||
}
|
||||
|
||||
this.ThrowPending();
|
||||
return InputChannel.HelpBeginReceive(this, timeout, callback, state);
|
||||
}
|
||||
|
||||
public Message EndReceive(IAsyncResult result)
|
||||
{
|
||||
return InputChannel.HelpEndReceive(result);
|
||||
}
|
||||
|
||||
public bool TryReceive(TimeSpan timeout, out Message message)
|
||||
{
|
||||
if (timeout < TimeSpan.Zero)
|
||||
{
|
||||
throw FxTrace.Exception.AsError(new ArgumentOutOfRangeException("timeout", timeout, SR.TimeoutOutOfRange0));
|
||||
}
|
||||
|
||||
this.ThrowPending();
|
||||
return base.Dequeue(timeout, out message);
|
||||
}
|
||||
|
||||
public IAsyncResult BeginTryReceive(TimeSpan timeout, AsyncCallback callback, object state)
|
||||
{
|
||||
if (timeout < TimeSpan.Zero)
|
||||
{
|
||||
throw FxTrace.Exception.AsError(new ArgumentOutOfRangeException("timeout", timeout, SR.TimeoutOutOfRange0));
|
||||
}
|
||||
|
||||
this.ThrowPending();
|
||||
return base.BeginDequeue(timeout, callback, state);
|
||||
}
|
||||
|
||||
public bool EndTryReceive(IAsyncResult result, out Message message)
|
||||
{
|
||||
return base.EndDequeue(result, out message);
|
||||
}
|
||||
|
||||
public bool WaitForMessage(TimeSpan timeout)
|
||||
{
|
||||
if (timeout < TimeSpan.Zero)
|
||||
{
|
||||
throw FxTrace.Exception.AsError(new ArgumentOutOfRangeException("timeout", timeout, SR.TimeoutOutOfRange0));
|
||||
}
|
||||
|
||||
this.ThrowPending();
|
||||
return base.WaitForItem(timeout);
|
||||
}
|
||||
|
||||
public IAsyncResult BeginWaitForMessage(TimeSpan timeout, AsyncCallback callback, object state)
|
||||
{
|
||||
if (timeout < TimeSpan.Zero)
|
||||
{
|
||||
throw FxTrace.Exception.AsError(new ArgumentOutOfRangeException("timeout", timeout, SR.TimeoutOutOfRange0));
|
||||
}
|
||||
|
||||
this.ThrowPending();
|
||||
return base.BeginWaitForItem(timeout, callback, state);
|
||||
}
|
||||
|
||||
public bool EndWaitForMessage(IAsyncResult result)
|
||||
{
|
||||
return base.EndWaitForItem(result);
|
||||
}
|
||||
|
||||
internal override void FinishEnqueueMessage(Message message, Action dequeuedCallback, bool canDispatchOnThisThread)
|
||||
{
|
||||
if (!this.IsMulticast)
|
||||
{
|
||||
//When using Multicast, we can't assume that receiving one message means that we are done receiving messages.
|
||||
//For example, Discovery will send one message out and receive n responses that match. Because of this, we
|
||||
//can only short circuit retransmission when using unicast.
|
||||
this.UdpOutputChannel.CancelRetransmission(message.Headers.RelatesTo);
|
||||
}
|
||||
this.EnqueueAndDispatch(message, dequeuedCallback, canDispatchOnThisThread);
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user