Merge pull request #394 from Azure/dev

Merge dev branch to master for 3.0 preview release
This commit is contained in:
vinaysurya 2017-12-15 16:59:28 -08:00 коммит произвёл GitHub
Родитель 87148d79cd a51efe26a2
Коммит df83179e19
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
51 изменённых файлов: 4159 добавлений и 1330 удалений

Просмотреть файл

@ -24,7 +24,7 @@ namespace Microsoft.Azure.ServiceBus
{ {
this.clientTypeName = clientTypeName; this.clientTypeName = clientTypeName;
this.ClientId = GenerateClientId(clientTypeName, postfix); this.ClientId = GenerateClientId(clientTypeName, postfix);
this.RetryPolicy = retryPolicy ?? throw new ArgumentNullException(nameof(retryPolicy)); this.RetryPolicy = retryPolicy ?? RetryPolicy.Default;
this.syncLock = new object(); this.syncLock = new object();
} }

Просмотреть файл

@ -36,5 +36,14 @@ namespace Microsoft.Azure.ServiceBus
public static readonly TimeSpan DefaultRetryDeltaBackoff = TimeSpan.FromSeconds(3); public static readonly TimeSpan DefaultRetryDeltaBackoff = TimeSpan.FromSeconds(3);
public static readonly TimeSpan NoMessageBackoffTimeSpan = TimeSpan.FromSeconds(5); public static readonly TimeSpan NoMessageBackoffTimeSpan = TimeSpan.FromSeconds(5);
public const string SasTokenType = "servicebus.windows.net:sastoken";
public const string JsonWebTokenType = "jwt";
public const string AadServiceBusAudience = "https://servicebus.azure.net/";
/// Represents 00:00:00 UTC Thursday 1, January 1970.
public static readonly DateTime EpochTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
} }
} }

Просмотреть файл

@ -5,6 +5,7 @@ namespace Microsoft.Azure.ServiceBus.Core
{ {
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -48,6 +49,7 @@ namespace Microsoft.Azure.ServiceBus.Core
readonly object messageReceivePumpSyncLock; readonly object messageReceivePumpSyncLock;
readonly bool ownsConnection; readonly bool ownsConnection;
readonly ActiveClientLinkManager clientLinkManager; readonly ActiveClientLinkManager clientLinkManager;
readonly ServiceBusDiagnosticSource diagnosticSource;
int prefetchCount; int prefetchCount;
long lastPeekedSequenceNumber; long lastPeekedSequenceNumber;
@ -132,7 +134,7 @@ namespace Microsoft.Azure.ServiceBus.Core
this.PrefetchCount = prefetchCount; this.PrefetchCount = prefetchCount;
this.messageReceivePumpSyncLock = new object(); this.messageReceivePumpSyncLock = new object();
this.clientLinkManager = new ActiveClientLinkManager(this.ClientId, this.CbsTokenProvider); this.clientLinkManager = new ActiveClientLinkManager(this.ClientId, this.CbsTokenProvider);
this.diagnosticSource = new ServiceBusDiagnosticSource(entityPath, serviceBusConnection.Endpoint);
MessagingEventSource.Log.MessageReceiverCreateStop(serviceBusConnection.Endpoint.Authority, entityPath, this.ClientId); MessagingEventSource.Log.MessageReceiverCreateStop(serviceBusConnection.Endpoint.Authority, entityPath, this.ClientId);
} }
@ -298,21 +300,36 @@ namespace Microsoft.Azure.ServiceBus.Core
MessagingEventSource.Log.MessageReceiveStart(this.ClientId, maxMessageCount); MessagingEventSource.Log.MessageReceiveStart(this.ClientId, maxMessageCount);
bool isDiagnosticSourceEnabled = ServiceBusDiagnosticSource.IsEnabled();
Activity activity = isDiagnosticSourceEnabled ? this.diagnosticSource.ReceiveStart(maxMessageCount) : null;
Task receiveTask = null;
IList<Message> unprocessedMessageList = null; IList<Message> unprocessedMessageList = null;
try try
{ {
await this.RetryPolicy.RunOperation( receiveTask = this.RetryPolicy.RunOperation(
async () => async () =>
{ {
unprocessedMessageList = await this.OnReceiveAsync(maxMessageCount, operationTimeout).ConfigureAwait(false); unprocessedMessageList = await this.OnReceiveAsync(maxMessageCount, operationTimeout)
}, operationTimeout) .ConfigureAwait(false);
.ConfigureAwait(false); }, operationTimeout);
await receiveTask.ConfigureAwait(false);
} }
catch (Exception exception) catch (Exception exception)
{ {
if (isDiagnosticSourceEnabled)
{
this.diagnosticSource.ReportException(exception);
}
MessagingEventSource.Log.MessageReceiveException(this.ClientId, exception); MessagingEventSource.Log.MessageReceiveException(this.ClientId, exception);
throw; throw;
} }
finally
{
this.diagnosticSource.ReceiveStop(activity, maxMessageCount, receiveTask?.Status, unprocessedMessageList);
}
MessagingEventSource.Log.MessageReceiveStop(this.ClientId, unprocessedMessageList?.Count ?? 0); MessagingEventSource.Log.MessageReceiveStop(this.ClientId, unprocessedMessageList?.Count ?? 0);
@ -366,22 +383,34 @@ namespace Microsoft.Azure.ServiceBus.Core
MessagingEventSource.Log.MessageReceiveDeferredMessageStart(this.ClientId, sequenceNumberList.Length, sequenceNumberList); MessagingEventSource.Log.MessageReceiveDeferredMessageStart(this.ClientId, sequenceNumberList.Length, sequenceNumberList);
bool isDiagnosticSourceEnabled = ServiceBusDiagnosticSource.IsEnabled();
Activity activity = isDiagnosticSourceEnabled ? this.diagnosticSource.ReceiveDeferredStart(sequenceNumberList) : null;
Task receiveTask = null;
IList<Message> messages = null; IList<Message> messages = null;
try try
{ {
await this.RetryPolicy.RunOperation( receiveTask = this.RetryPolicy.RunOperation(
async () => async () =>
{ {
messages = await this.OnReceiveDeferredMessageAsync(sequenceNumberList).ConfigureAwait(false); messages = await this.OnReceiveDeferredMessageAsync(sequenceNumberList).ConfigureAwait(false);
}, this.OperationTimeout) }, this.OperationTimeout);
.ConfigureAwait(false); await receiveTask.ConfigureAwait(false);
} }
catch (Exception exception) catch (Exception exception)
{ {
if (isDiagnosticSourceEnabled)
{
this.diagnosticSource.ReportException(exception);
}
MessagingEventSource.Log.MessageReceiveDeferredMessageException(this.ClientId, exception); MessagingEventSource.Log.MessageReceiveDeferredMessageException(this.ClientId, exception);
throw; throw;
} }
finally
{
this.diagnosticSource.ReceiveDeferredStop(activity, sequenceNumberList, receiveTask?.Status, messages);
}
MessagingEventSource.Log.MessageReceiveDeferredMessageStop(this.ClientId, messages?.Count ?? 0); MessagingEventSource.Log.MessageReceiveDeferredMessageStop(this.ClientId, messages?.Count ?? 0);
return messages; return messages;
@ -425,17 +454,31 @@ namespace Microsoft.Azure.ServiceBus.Core
} }
MessagingEventSource.Log.MessageCompleteStart(this.ClientId, lockTokenList.Count, lockTokenList); MessagingEventSource.Log.MessageCompleteStart(this.ClientId, lockTokenList.Count, lockTokenList);
bool isDiagnosticSourceEnabled = ServiceBusDiagnosticSource.IsEnabled();
Activity activity = isDiagnosticSourceEnabled ? this.diagnosticSource.CompleteStart(lockTokenList) : null;
Task completeTask = null;
try try
{ {
await this.RetryPolicy.RunOperation(() => this.OnCompleteAsync(lockTokenList), this.OperationTimeout) completeTask =
.ConfigureAwait(false); this.RetryPolicy.RunOperation(() => this.OnCompleteAsync(lockTokenList), this.OperationTimeout);
await completeTask.ConfigureAwait(false);
} }
catch (Exception exception) catch (Exception exception)
{ {
if (isDiagnosticSourceEnabled)
{
this.diagnosticSource.ReportException(exception);
}
MessagingEventSource.Log.MessageCompleteException(this.ClientId, exception); MessagingEventSource.Log.MessageCompleteException(this.ClientId, exception);
throw; throw;
} }
finally
{
this.diagnosticSource.CompleteStop(activity, lockTokenList, completeTask?.Status);
}
MessagingEventSource.Log.MessageCompleteStop(this.ClientId); MessagingEventSource.Log.MessageCompleteStop(this.ClientId);
} }
@ -456,16 +499,31 @@ namespace Microsoft.Azure.ServiceBus.Core
this.ThrowIfNotPeekLockMode(); this.ThrowIfNotPeekLockMode();
MessagingEventSource.Log.MessageAbandonStart(this.ClientId, 1, lockToken); MessagingEventSource.Log.MessageAbandonStart(this.ClientId, 1, lockToken);
bool isDiagnosticSourceEnabled = ServiceBusDiagnosticSource.IsEnabled();
Activity activity = isDiagnosticSourceEnabled ? this.diagnosticSource.DisposeStart("Abandon", lockToken) : null;
Task abandonTask = null;
try try
{ {
await this.RetryPolicy.RunOperation(() => this.OnAbandonAsync(lockToken, propertiesToModify), this.OperationTimeout) abandonTask = this.RetryPolicy.RunOperation(() => this.OnAbandonAsync(lockToken, propertiesToModify),
.ConfigureAwait(false); this.OperationTimeout);
await abandonTask.ConfigureAwait(false);
} }
catch (Exception exception) catch (Exception exception)
{ {
if (isDiagnosticSourceEnabled)
{
this.diagnosticSource.ReportException(exception);
}
MessagingEventSource.Log.MessageAbandonException(this.ClientId, exception); MessagingEventSource.Log.MessageAbandonException(this.ClientId, exception);
throw; throw;
} }
finally
{
this.diagnosticSource.DisposeStop(activity, lockToken, abandonTask?.Status);
}
MessagingEventSource.Log.MessageAbandonStop(this.ClientId); MessagingEventSource.Log.MessageAbandonStop(this.ClientId);
} }
@ -487,18 +545,30 @@ namespace Microsoft.Azure.ServiceBus.Core
this.ThrowIfNotPeekLockMode(); this.ThrowIfNotPeekLockMode();
MessagingEventSource.Log.MessageDeferStart(this.ClientId, 1, lockToken); MessagingEventSource.Log.MessageDeferStart(this.ClientId, 1, lockToken);
bool isDiagnosticSourceEnabled = ServiceBusDiagnosticSource.IsEnabled();
Activity activity = isDiagnosticSourceEnabled ? this.diagnosticSource.DisposeStart("Defer", lockToken) : null;
Task deferTask = null;
try try
{ {
await this.RetryPolicy.RunOperation(() => this.OnDeferAsync(lockToken, propertiesToModify), this.OperationTimeout) deferTask = this.RetryPolicy.RunOperation(() => this.OnDeferAsync(lockToken, propertiesToModify),
.ConfigureAwait(false); this.OperationTimeout);
await deferTask.ConfigureAwait(false);
} }
catch (Exception exception) catch (Exception exception)
{ {
if (isDiagnosticSourceEnabled)
{
this.diagnosticSource.ReportException(exception);
}
MessagingEventSource.Log.MessageDeferException(this.ClientId, exception); MessagingEventSource.Log.MessageDeferException(this.ClientId, exception);
throw; throw;
} }
finally
{
this.diagnosticSource.DisposeStop(activity, lockToken, deferTask?.Status);
}
MessagingEventSource.Log.MessageDeferStop(this.ClientId); MessagingEventSource.Log.MessageDeferStop(this.ClientId);
} }
@ -520,18 +590,30 @@ namespace Microsoft.Azure.ServiceBus.Core
this.ThrowIfNotPeekLockMode(); this.ThrowIfNotPeekLockMode();
MessagingEventSource.Log.MessageDeadLetterStart(this.ClientId, 1, lockToken); MessagingEventSource.Log.MessageDeadLetterStart(this.ClientId, 1, lockToken);
bool isDiagnosticSourceEnabled = ServiceBusDiagnosticSource.IsEnabled();
Activity activity = isDiagnosticSourceEnabled ? this.diagnosticSource.DisposeStart("DeadLetter", lockToken) : null;
Task deadLetterTask = null;
try try
{ {
await this.RetryPolicy.RunOperation(() => this.OnDeadLetterAsync(lockToken, propertiesToModify), this.OperationTimeout) deadLetterTask = this.RetryPolicy.RunOperation(() => this.OnDeadLetterAsync(lockToken, propertiesToModify),
.ConfigureAwait(false); this.OperationTimeout);
await deadLetterTask.ConfigureAwait(false);
} }
catch (Exception exception) catch (Exception exception)
{ {
if (isDiagnosticSourceEnabled)
{
this.diagnosticSource.ReportException(exception);
}
MessagingEventSource.Log.MessageDeadLetterException(this.ClientId, exception); MessagingEventSource.Log.MessageDeadLetterException(this.ClientId, exception);
throw; throw;
} }
finally
{
this.diagnosticSource.DisposeStop(activity, lockToken, deadLetterTask?.Status);
}
MessagingEventSource.Log.MessageDeadLetterStop(this.ClientId); MessagingEventSource.Log.MessageDeadLetterStop(this.ClientId);
} }
@ -554,17 +636,32 @@ namespace Microsoft.Azure.ServiceBus.Core
this.ThrowIfNotPeekLockMode(); this.ThrowIfNotPeekLockMode();
MessagingEventSource.Log.MessageDeadLetterStart(this.ClientId, 1, lockToken); MessagingEventSource.Log.MessageDeadLetterStart(this.ClientId, 1, lockToken);
bool isDiagnosticSourceEnabled = ServiceBusDiagnosticSource.IsEnabled();
Activity activity = isDiagnosticSourceEnabled ? this.diagnosticSource.DisposeStart("DeadLetter", lockToken) : null;
Task deadLetterTask = null;
try try
{ {
await this.RetryPolicy.RunOperation(() => this.OnDeadLetterAsync(lockToken, null, deadLetterReason, deadLetterErrorDescription), this.OperationTimeout) deadLetterTask =
.ConfigureAwait(false); this.RetryPolicy.RunOperation(
() => this.OnDeadLetterAsync(lockToken, null, deadLetterReason, deadLetterErrorDescription),
this.OperationTimeout);
await deadLetterTask.ConfigureAwait(false);
} }
catch (Exception exception) catch (Exception exception)
{ {
if (isDiagnosticSourceEnabled)
{
this.diagnosticSource.ReportException(exception);
}
MessagingEventSource.Log.MessageDeadLetterException(this.ClientId, exception); MessagingEventSource.Log.MessageDeadLetterException(this.ClientId, exception);
throw; throw;
} }
finally
{
this.diagnosticSource.DisposeStop(activity, lockToken, deadLetterTask?.Status);
}
MessagingEventSource.Log.MessageDeadLetterStop(this.ClientId); MessagingEventSource.Log.MessageDeadLetterStop(this.ClientId);
} }
@ -600,21 +697,33 @@ namespace Microsoft.Azure.ServiceBus.Core
this.ThrowIfNotPeekLockMode(); this.ThrowIfNotPeekLockMode();
MessagingEventSource.Log.MessageRenewLockStart(this.ClientId, 1, lockToken); MessagingEventSource.Log.MessageRenewLockStart(this.ClientId, 1, lockToken);
bool isDiagnosticSourceEnabled = ServiceBusDiagnosticSource.IsEnabled();
Activity activity = isDiagnosticSourceEnabled ? this.diagnosticSource.RenewLockStart(lockToken) : null;
Task renewTask = null;
var lockedUntilUtc = DateTime.MinValue; var lockedUntilUtc = DateTime.MinValue;
try try
{ {
await this.RetryPolicy.RunOperation( renewTask = this.RetryPolicy.RunOperation(
async () => lockedUntilUtc = await this.OnRenewLockAsync(lockToken).ConfigureAwait(false), this.OperationTimeout) async () => lockedUntilUtc = await this.OnRenewLockAsync(lockToken).ConfigureAwait(false),
.ConfigureAwait(false); this.OperationTimeout);
await renewTask.ConfigureAwait(false);
} }
catch (Exception exception) catch (Exception exception)
{ {
if (isDiagnosticSourceEnabled)
{
this.diagnosticSource.ReportException(exception);
}
MessagingEventSource.Log.MessageRenewLockException(this.ClientId, exception); MessagingEventSource.Log.MessageRenewLockException(this.ClientId, exception);
throw; throw;
} }
finally
{
this.diagnosticSource.RenewLockStop(activity, lockToken, renewTask?.Status, lockedUntilUtc);
}
MessagingEventSource.Log.MessageRenewLockStop(this.ClientId); MessagingEventSource.Log.MessageRenewLockStop(this.ClientId);
return lockedUntilUtc; return lockedUntilUtc;
@ -671,20 +780,34 @@ namespace Microsoft.Azure.ServiceBus.Core
IList<Message> messages = null; IList<Message> messages = null;
MessagingEventSource.Log.MessagePeekStart(this.ClientId, fromSequenceNumber, messageCount); MessagingEventSource.Log.MessagePeekStart(this.ClientId, fromSequenceNumber, messageCount);
bool isDiagnosticSourceEnabled = ServiceBusDiagnosticSource.IsEnabled();
Activity activity = isDiagnosticSourceEnabled ? this.diagnosticSource.PeekStart(fromSequenceNumber, messageCount) : null;
Task peekTask = null;
try try
{ {
await this.RetryPolicy.RunOperation( peekTask = this.RetryPolicy.RunOperation(
async () => async () =>
{ {
messages = await this.OnPeekAsync(fromSequenceNumber, messageCount).ConfigureAwait(false); messages = await this.OnPeekAsync(fromSequenceNumber, messageCount).ConfigureAwait(false);
}, this.OperationTimeout) }, this.OperationTimeout);
.ConfigureAwait(false);
await peekTask.ConfigureAwait(false);
} }
catch (Exception exception) catch (Exception exception)
{ {
if (isDiagnosticSourceEnabled)
{
this.diagnosticSource.ReportException(exception);
}
MessagingEventSource.Log.MessagePeekException(this.ClientId, exception); MessagingEventSource.Log.MessagePeekException(this.ClientId, exception);
throw; throw;
} }
finally
{
this.diagnosticSource.PeekStop(activity, fromSequenceNumber, messageCount, peekTask?.Status, messages);
}
MessagingEventSource.Log.MessagePeekStop(this.ClientId, messages?.Count ?? 0); MessagingEventSource.Log.MessagePeekStop(this.ClientId, messages?.Count ?? 0);
return messages; return messages;
@ -1017,7 +1140,7 @@ namespace Microsoft.Azure.ServiceBus.Core
{ {
throw new ArgumentOutOfRangeException(nameof(deadLetterErrorDescription), $"Maximum permitted length is {Constants.MaxDeadLetterReasonLength}"); throw new ArgumentOutOfRangeException(nameof(deadLetterErrorDescription), $"Maximum permitted length is {Constants.MaxDeadLetterReasonLength}");
} }
var lockTokens = new[] { new Guid(lockToken) }; var lockTokens = new[] { new Guid(lockToken) };
if (lockTokens.Any(lt => this.requestResponseLockedMessages.Contains(lt))) if (lockTokens.Any(lt => this.requestResponseLockedMessages.Contains(lt)))
{ {
@ -1071,7 +1194,7 @@ namespace Microsoft.Azure.ServiceBus.Core
} }
this.receivePumpCancellationTokenSource = new CancellationTokenSource(); this.receivePumpCancellationTokenSource = new CancellationTokenSource();
this.receivePump = new MessageReceivePump(this, registerHandlerOptions, callback, this.ServiceBusConnection.Endpoint.Authority, this.receivePumpCancellationTokenSource.Token); this.receivePump = new MessageReceivePump(this, registerHandlerOptions, callback, this.ServiceBusConnection.Endpoint, this.receivePumpCancellationTokenSource.Token);
} }
try try
@ -1246,7 +1369,8 @@ namespace Microsoft.Azure.ServiceBus.Core
} }
else else
{ {
throw new NotSupportedException(Resources.InvalidAmqpMessageProperty.FormatForUser(pair.Key.GetType())); throw new NotSupportedException(
Resources.InvalidAmqpMessageProperty.FormatForUser(pair.Key.GetType()));
} }
} }

Просмотреть файл

@ -5,6 +5,7 @@ namespace Microsoft.Azure.ServiceBus.Core
{ {
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -37,6 +38,7 @@ namespace Microsoft.Azure.ServiceBus.Core
int deliveryCount; int deliveryCount;
readonly bool ownsConnection; readonly bool ownsConnection;
readonly ActiveClientLinkManager clientLinkManager; readonly ActiveClientLinkManager clientLinkManager;
readonly ServiceBusDiagnosticSource diagnosticSource;
/// <summary> /// <summary>
/// Creates a new AMQP MessageSender. /// Creates a new AMQP MessageSender.
@ -96,6 +98,7 @@ namespace Microsoft.Azure.ServiceBus.Core
this.SendLinkManager = new FaultTolerantAmqpObject<SendingAmqpLink>(this.CreateLinkAsync, CloseSession); this.SendLinkManager = new FaultTolerantAmqpObject<SendingAmqpLink>(this.CreateLinkAsync, CloseSession);
this.RequestResponseLinkManager = new FaultTolerantAmqpObject<RequestResponseAmqpLink>(this.CreateRequestResponseLinkAsync, CloseRequestResponseSession); this.RequestResponseLinkManager = new FaultTolerantAmqpObject<RequestResponseAmqpLink>(this.CreateRequestResponseLinkAsync, CloseRequestResponseSession);
this.clientLinkManager = new ActiveClientLinkManager(this.ClientId, this.CbsTokenProvider); this.clientLinkManager = new ActiveClientLinkManager(this.ClientId, this.CbsTokenProvider);
this.diagnosticSource = new ServiceBusDiagnosticSource(entityPath, serviceBusConnection.Endpoint);
MessagingEventSource.Log.MessageSenderCreateStop(serviceBusConnection.Endpoint.Authority, entityPath, this.ClientId); MessagingEventSource.Log.MessageSenderCreateStop(serviceBusConnection.Endpoint.Authority, entityPath, this.ClientId);
} }
@ -144,21 +147,35 @@ namespace Microsoft.Azure.ServiceBus.Core
public async Task SendAsync(IList<Message> messageList) public async Task SendAsync(IList<Message> messageList)
{ {
this.ThrowIfClosed(); this.ThrowIfClosed();
var count = MessageSender.ValidateMessages(messageList); var count = MessageSender.ValidateMessages(messageList);
MessagingEventSource.Log.MessageSendStart(this.ClientId, count); MessagingEventSource.Log.MessageSendStart(this.ClientId, count);
var processedMessages = await this.ProcessMessages(messageList).ConfigureAwait(false); bool isDiagnosticSourceEnabled = ServiceBusDiagnosticSource.IsEnabled();
Activity activity = isDiagnosticSourceEnabled ? this.diagnosticSource.SendStart(messageList) : null;
Task sendTask = null;
try try
{ {
await this.RetryPolicy.RunOperation(() => this.OnSendAsync(processedMessages), this.OperationTimeout) var processedMessages = await this.ProcessMessages(messageList).ConfigureAwait(false);
.ConfigureAwait(false);
sendTask = this.RetryPolicy.RunOperation(() => this.OnSendAsync(processedMessages), this.OperationTimeout);
await sendTask.ConfigureAwait(false);
} }
catch (Exception exception) catch (Exception exception)
{ {
if (isDiagnosticSourceEnabled)
{
this.diagnosticSource.ReportException(exception);
}
MessagingEventSource.Log.MessageSendException(this.ClientId, exception); MessagingEventSource.Log.MessageSendException(this.ClientId, exception);
throw; throw;
} }
finally
{
this.diagnosticSource.SendStop(activity, messageList, sendTask?.Status);
}
MessagingEventSource.Log.MessageSendStop(this.ClientId); MessagingEventSource.Log.MessageSendStop(this.ClientId);
} }
@ -190,22 +207,35 @@ namespace Microsoft.Azure.ServiceBus.Core
MessagingEventSource.Log.ScheduleMessageStart(this.ClientId, scheduleEnqueueTimeUtc); MessagingEventSource.Log.ScheduleMessageStart(this.ClientId, scheduleEnqueueTimeUtc);
long result = 0; long result = 0;
var processedMessage = await this.ProcessMessage(message).ConfigureAwait(false); bool isDiagnosticSourceEnabled = ServiceBusDiagnosticSource.IsEnabled();
Activity activity = isDiagnosticSourceEnabled ? this.diagnosticSource.ScheduleStart(message, scheduleEnqueueTimeUtc) : null;
Task scheduleTask = null;
try try
{ {
await this.RetryPolicy.RunOperation( var processedMessage = await this.ProcessMessage(message).ConfigureAwait(false);
scheduleTask = this.RetryPolicy.RunOperation(
async () => async () =>
{ {
result = await this.OnScheduleMessageAsync(processedMessage).ConfigureAwait(false); result = await this.OnScheduleMessageAsync(processedMessage).ConfigureAwait(false);
}, this.OperationTimeout) }, this.OperationTimeout);
.ConfigureAwait(false); await scheduleTask.ConfigureAwait(false);
} }
catch (Exception exception) catch (Exception exception)
{ {
if (isDiagnosticSourceEnabled)
{
this.diagnosticSource.ReportException(exception);
}
MessagingEventSource.Log.ScheduleMessageException(this.ClientId, exception); MessagingEventSource.Log.ScheduleMessageException(this.ClientId, exception);
throw; throw;
} }
finally
{
this.diagnosticSource.ScheduleStop(activity, message, scheduleEnqueueTimeUtc, scheduleTask?.Status, result);
}
MessagingEventSource.Log.ScheduleMessageStop(this.ClientId); MessagingEventSource.Log.ScheduleMessageStop(this.ClientId);
return result; return result;
@ -220,17 +250,30 @@ namespace Microsoft.Azure.ServiceBus.Core
this.ThrowIfClosed(); this.ThrowIfClosed();
MessagingEventSource.Log.CancelScheduledMessageStart(this.ClientId, sequenceNumber); MessagingEventSource.Log.CancelScheduledMessageStart(this.ClientId, sequenceNumber);
bool isDiagnosticSourceEnabled = ServiceBusDiagnosticSource.IsEnabled();
Activity activity = isDiagnosticSourceEnabled ? this.diagnosticSource.CancelStart(sequenceNumber) : null;
Task cancelTask = null;
try try
{ {
await this.RetryPolicy.RunOperation(() => this.OnCancelScheduledMessageAsync(sequenceNumber), this.OperationTimeout) cancelTask = this.RetryPolicy.RunOperation(() => this.OnCancelScheduledMessageAsync(sequenceNumber),
.ConfigureAwait(false); this.OperationTimeout);
await cancelTask.ConfigureAwait(false);
} }
catch (Exception exception) catch (Exception exception)
{ {
if (isDiagnosticSourceEnabled)
{
this.diagnosticSource.ReportException(exception);
}
MessagingEventSource.Log.CancelScheduledMessageException(this.ClientId, exception); MessagingEventSource.Log.CancelScheduledMessageException(this.ClientId, exception);
throw; throw;
} }
finally
{
this.diagnosticSource.CancelStop(activity, sequenceNumber, cancelTask?.Status);
}
MessagingEventSource.Log.CancelScheduledMessageStop(this.ClientId); MessagingEventSource.Log.CancelScheduledMessageStop(this.ClientId);
} }
@ -403,6 +446,7 @@ namespace Microsoft.Azure.ServiceBus.Core
} }
var outcome = await amqpLink.SendMessageAsync(amqpMessage, this.GetNextDeliveryTag(), AmqpConstants.NullBinary, timeoutHelper.RemainingTime()).ConfigureAwait(false); var outcome = await amqpLink.SendMessageAsync(amqpMessage, this.GetNextDeliveryTag(), AmqpConstants.NullBinary, timeoutHelper.RemainingTime()).ConfigureAwait(false);
if (outcome.DescriptorCode != Accepted.Code) if (outcome.DescriptorCode != Accepted.Code)
{ {
var rejected = (Rejected)outcome; var rejected = (Rejected)outcome;

Просмотреть файл

@ -0,0 +1,153 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.Azure.ServiceBus.Diagnostics
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
public static class MessageExtensions
{
/// <summary>
/// Creates <see cref="Activity"/> based on the tracing context stored in the <see cref="Message"/>
/// <param name="activityName">Optional Activity name</param>
/// <returns>New <see cref="Activity"/> with tracing context</returns>
/// </summary>
/// <remarks>
/// Tracing context is used to correlate telemetry between producer and consumer and
/// represented by 'Diagnostic-Id' and 'Correlation-Context' properties in <see cref="Message.UserProperties"/>.
///
/// .NET SDK automatically injects context when sending message to the ServiceBus (if diagnostics is enabled by tracing system).
///
/// <para>
/// 'Diagnostic-Id' uniquely identifies operation that enqueued message
/// </para>
/// <para>
/// 'Correlation-Context' is comma separated list of sting key value pairs represeting optional context for the operation.
/// </para>
///
/// If there is no tracing context in the message, this method returns <see cref="Activity"/> without parent.
///
/// Returned <see cref="Activity"/> needs to be started before it can be used (see example below)
/// </remarks>
/// <example>
/// <code>
/// async Task ProcessAsync()
/// {
/// var message = await messageReceiver.ReceiveAsync();
/// var activity = message.ExtractActivity();
/// activity.Start();
/// Logger.LogInformation($"Message received, Id = {Activity.Current.Id}")
/// try
/// {
/// // process message
/// }
/// catch (Exception ex)
/// {
/// Logger.LogError($"Exception {ex}, Id = {Activity.Current.Id}")
/// }
/// finally
/// {
/// activity.Stop();
/// // Activity is stopped, we no longer have it in Activity.Current, let's user activity now
/// Logger.LogInformation($"Message processed, Id = {activity.Id}, Duration = {activity.Duration}")
/// }
/// }
/// </code>
///
/// Note that every log is stamped with <see cref="Activity.Current"/>.Id, that could be used within
/// any nested method call (sync or async) - <see cref="Activity.Current"/> is an ambient context that flows with async method calls.
///
/// </example>
public static Activity ExtractActivity(this Message message, string activityName = null)
{
if (message == null)
{
throw new ArgumentNullException(nameof(message));
}
if (activityName == null)
{
activityName = ServiceBusDiagnosticSource.ProcessActivityName;
}
var activity = new Activity(activityName);
if (TryExtractId(message, out string id))
{
activity.SetParentId(id);
if (message.TryExtractContext(out IList<KeyValuePair<string, string>> ctx))
{
foreach (var kvp in ctx)
{
activity.AddBaggage(kvp.Key, kvp.Value);
}
}
}
return activity;
}
internal static bool TryExtractId(this Message message, out string id)
{
id = null;
if (message.UserProperties.TryGetValue(ServiceBusDiagnosticSource.ActivityIdPropertyName,
out object requestId))
{
var tmp = requestId as string;
if (tmp != null && tmp.Trim().Length > 0)
{
id = tmp;
return true;
}
}
return false;
}
internal static bool TryExtractContext(this Message message, out IList<KeyValuePair<string, string>> context)
{
context = null;
try
{
if (message.UserProperties.TryGetValue(ServiceBusDiagnosticSource.CorrelationContextPropertyName,
out object ctxObj))
{
string ctxStr = ctxObj as string;
if (string.IsNullOrEmpty(ctxStr))
{
return false;
}
var ctxList = ctxStr.Split(',');
if (ctxList.Length == 0)
{
return false;
}
context = new List<KeyValuePair<string, string>>();
foreach (string item in ctxList)
{
var kvp = item.Split('=');
if (kvp.Length == 2)
{
context.Add(new KeyValuePair<string, string>(kvp[0], kvp[1]));
}
}
return true;
}
}
catch (Exception)
{
// ignored, if context is invalid, there nothing we can do:
// invalid context was created by consumer, but if we throw here, it will break message processing on producer
// and producer does not control which context it receives
}
return false;
}
}
}

Просмотреть файл

@ -9,8 +9,11 @@ namespace Microsoft.Azure.ServiceBus
using Primitives; using Primitives;
/// <summary> /// <summary>
/// The object used to communicate and transfer data with Service Bus. /// The message object used to communicate and transfer data with Service Bus.
/// </summary> /// </summary>
/// <remarks>
/// The message structure is discussed in detail in the <a href="https://docs.microsoft.com/azure/service-bus-messaging/service-bus-messages-payloads">product documentation.</a>
/// </remarks>
public class Message public class Message
{ {
/// <summary> /// <summary>
@ -63,8 +66,14 @@ namespace Microsoft.Azure.ServiceBus
/// <summary> /// <summary>
/// Gets or sets the MessageId to identify the message. /// Gets or sets the MessageId to identify the message.
/// </summary> /// </summary>
/// <remarks>A value set by the user to identify the message. In case message deduplication is enabled on the entity, this value will be used for deduplication. /// <remarks>
/// Max MessageId size is 128 chars.</remarks> /// The message identifier is an application-defined value that uniquely identifies the
/// message and its payload. The identifier is a free-form string and can reflect a GUID
/// or an identifier derived from the application context. If enabled, the
/// <a href="https://docs.microsoft.com/azure/service-bus-messaging/duplicate-detection">duplicate detection</a>
/// feature identifies and removes second and further submissions of messages with the
/// same MessageId.
/// </remarks>
public string MessageId public string MessageId
{ {
get => this.messageId; get => this.messageId;
@ -76,10 +85,14 @@ namespace Microsoft.Azure.ServiceBus
} }
} }
/// <summary>Gets or sets a partition key for sending a transactional message to a queue or topic that is not session-aware.</summary> /// <summary>Gets or sets a partition key for sending a message to a partitioned entity.</summary>
/// <value>The partition key for sending a transactional message.</value> /// <value>The partition key. Maximum length is 128 characters.</value>
/// <remarks>Transactions are not currently supported with this library. Messages with same partitionKey are sent to the same partition. /// <remarks>
/// Max PartitionKey size is 128 chars.</remarks> /// For <a href="https://docs.microsoft.com/azure/service-bus-messaging/service-bus-partitioning">partitioned entities</a>,
/// setting this value enables assigning related messages to the same internal partition, so that submission sequence
/// order is correctly recorded. The partition is chosen by a hash function over this value and cannot be chosen
/// directly. For session-aware entities, the <see cref="SessionId"/> property overrides this value.
/// </remarks>
public string PartitionKey public string PartitionKey
{ {
get => this.partitionKey; get => this.partitionKey;
@ -91,9 +104,14 @@ namespace Microsoft.Azure.ServiceBus
} }
} }
/// <summary>Gets or sets a partition key for sending a transactional message via a transfer queue.</summary> /// <summary>Gets or sets a partition key for sending a message into an entity via a partitioned transfer queue.</summary>
/// <value>The partition key value when a transaction is to be used to send messages via a transfer queue.</value> /// <value>The partition key. Maximum length is 128 characters. </value>
/// <remarks>Max size of ViaPartitionKey is 128 chars.</remarks> /// <remarks>
/// If a message is sent via a transfer queue in the scope of a transaction, this value selects the
/// transfer queue partition: This is functionally equivalent to <see cref="PartitionKey"/> and ensures that
/// messages are kept together and in order as they are transferred.
/// See <a href="https://docs.microsoft.com/azure/service-bus-messaging/service-bus-transactions#transfers-and-send-via">Transfers and Send Via</a>.
/// </remarks>
public string ViaPartitionKey public string ViaPartitionKey
{ {
get => this.viaPartitionKey; get => this.viaPartitionKey;
@ -105,9 +123,15 @@ namespace Microsoft.Azure.ServiceBus
} }
} }
/// <summary>Gets or sets a sessionId. A message with sessionId set can only be received using a <see cref="IMessageSession"/> object.</summary> /// <summary>Gets or sets the session identifier for a session-aware entity.</summary>
/// <value>The identifier of the session.</value> /// <value>The session identifier. Maximum length is 128 characters.</value>
/// <remarks>Max size of sessionId is 128 chars.</remarks> /// <remarks>
/// For session-aware entities, this application-defined value specifies the session
/// affiliation of the message. Messages with the same session identifier are subject
/// to summary locking and enable exact in-order processing and demultiplexing.
/// For session-unaware entities, this value is ignored.
/// See <a href="https://docs.microsoft.com/azure/service-bus-messaging/message-sessions">Message Sessions</a>.
/// </remarks>
public string SessionId public string SessionId
{ {
get => this.sessionId; get => this.sessionId;
@ -119,9 +143,12 @@ namespace Microsoft.Azure.ServiceBus
} }
} }
/// <summary>Gets or sets the session identifier to reply to.</summary> /// <summary>Gets or sets a session identifier augmenting the <see cref="ReplyTo"/> address.</summary>
/// <value>The session identifier to reply to.</value> /// <value>Session identifier. Maximum length is 128 characters.</value>
/// <remarks>Max size of ReplyToSessionId is 128.</remarks> /// <remarks>
/// This value augments the ReplyTo information and specifies which SessionId should be set
/// for the reply when sent to the reply entity. See <a href="https://docs.microsoft.com/azure/service-bus-messaging/service-bus-messages-payloads?#message-routing-and-correlation">Message Routing and Correlation</a>
/// </remarks>
public string ReplyToSessionId public string ReplyToSessionId
{ {
get => this.replyToSessionId; get => this.replyToSessionId;
@ -134,9 +161,12 @@ namespace Microsoft.Azure.ServiceBus
} }
/// <summary>Gets the date and time in UTC at which the message is set to expire.</summary> /// <summary>Gets the date and time in UTC at which the message is set to expire.</summary>
/// <value>The message expiration time in UTC.</value> /// <value>The message expiration time in UTC. This property is read-only.</value>
/// <exception cref="System.InvalidOperationException">If the message has not been received. For example if a new message was created but not yet sent and received.</exception> /// <exception cref="System.InvalidOperationException">If the message has not been received. For example if a new message was created but not yet sent and received.</exception>
/// <remarks>Unless specifically set for a message, this value is controlled by the 'DefaultMessageTimeToLive' property set while creating the entity.</remarks> /// <remarks>
/// The UTC instant at which the message is marked for removal and no longer available for retrieval
/// from the entity due to expiration. Expiry is controlled by the <see cref="TimeToLive"/> property
/// and this property is computed from <see cref="SystemPropertiesCollection.EnqueuedTimeUtc"/>+<see cref="TimeToLive"/></remarks>
public DateTime ExpiresAtUtc public DateTime ExpiresAtUtc
{ {
get get
@ -151,14 +181,17 @@ namespace Microsoft.Azure.ServiceBus
} }
/// <summary> /// <summary>
/// Gets or sets the messages time to live value. This is the duration after which the message expires, starting from when the message is sent to the Service Bus. /// Gets or sets the messages "time to live" value.
/// Messages older than their TimeToLive value will expire and no longer be retained in the message store. Expired messages cannot be received.
/// TimeToLive is the maximum lifetime that a message can be received, but its value cannot exceed the entity specified value on the destination queue or subscription.
/// If a lower TimeToLive value is specified, it will be applied to the individual message. However, a larger value specified on the message will be overridden by the
/// entitys DefaultMessageTimeToLive value.
/// </summary> /// </summary>
/// <value>The messages time to live value.</value> /// <value>The messages time to live value.</value>
/// <remarks>If the TTL set on a message by the sender exceeds the destination's TTL, then the message's TTL will be overwritten by the later one.</remarks> /// <remarks>
/// This value is the relative duration after which the message expires, starting from the instant
/// the message has been accepted and stored by the broker, as captured in <see cref="SystemPropertiesCollection.EnqueuedTimeUtc"/>.
/// When not set explicitly, the assumed value is the DefaultTimeToLive for the respective queue or topic.
/// A message-level <see cref="TimeToLive"/> value cannot be longer than the entity's DefaultTimeToLive
/// setting and it is silently adjusted if it does.
/// See <a href="https://docs.microsoft.com/azure/service-bus-messaging/message-expiration">Expiration</a>
/// </remarks>
public TimeSpan TimeToLive public TimeSpan TimeToLive
{ {
get get
@ -179,25 +212,48 @@ namespace Microsoft.Azure.ServiceBus
} }
/// <summary>Gets or sets the a correlation identifier.</summary> /// <summary>Gets or sets the a correlation identifier.</summary>
/// <value>The identifier of the correlation.</value> /// <value>Correlation identifier.</value>
/// <remarks>Its a custom property that can be used to either transfer a correlation Id to the destination or be used in <see cref="CorrelationFilter"/></remarks> /// <remarks>
/// Allows an application to specify a context for the message for the purposes of correlation,
/// for example reflecting the MessageId of a message that is being replied to.
/// See <a href="https://docs.microsoft.com/azure/service-bus-messaging/service-bus-messages-payloads?#message-routing-and-correlation">Message Routing and Correlation</a>.
/// </remarks>
public string CorrelationId { get; set; } public string CorrelationId { get; set; }
/// <summary>Gets or sets the application specific label.</summary> /// <summary>Gets or sets an application specific label.</summary>
/// <value>The application specific label.</value> /// <value>The application specific label</value>
/// <remarks>
/// This property enables the application to indicate the purpose of the message to the receiver in a standardized
/// fashion, similar to an email subject line. The mapped AMQP property is "subject".
/// </remarks>
public string Label { get; set; } public string Label { get; set; }
/// <summary>Gets or sets the send to address.</summary> /// <summary>Gets or sets the "to" address.</summary>
/// <value>The send to address.</value> /// <value>The "to" address.</value>
/// <remarks>
/// This property is reserved for future use in routing scenarios and presently ignored by the broker itself.
/// Applications can use this value in rule-driven
/// <a href="https://docs.microsoft.com/azure/service-bus-messaging/service-bus-auto-forwarding">auto-forward chaining</a> scenarios to indicate the
/// intended logical destination of the message.
/// </remarks>
public string To { get; set; } public string To { get; set; }
/// <summary>Gets or sets the type of the content.</summary> /// <summary>Gets or sets the content tpye descriptor.</summary>
/// <value>The type of the content of the message body. This is a /// <value>RFC2045 Content-Type descriptor.</value>
/// content type identifier utilized by the sender and receiver for application specific logic.</value> /// <remarks>
/// Optionally describes the payload of the message, with a descriptor following the format of
/// RFC2045, Section 5, for example "application/json".
/// </remarks>
public string ContentType { get; set; } public string ContentType { get; set; }
/// <summary>Gets or sets the address of the queue to reply to.</summary> /// <summary>Gets or sets the address of an entity to send replies to.</summary>
/// <value>The reply to queue address.</value> /// <value>The reply entity address.</value>
/// <remarks>
/// This optional and application-defined value is a standard way to express a reply path
/// to the receiver of the message. When a sender expects a reply, it sets the value to the
/// absolute or relative path of the queue or topic it expects the reply to be sent to.
/// See <a href="https://docs.microsoft.com/azure/service-bus-messaging/service-bus-messages-payloads?#message-routing-and-correlation">Message Routing and Correlation</a>.
/// </remarks>
public string ReplyTo { get; set; } public string ReplyTo { get; set; }
/// <summary>Gets or sets the date and time in UTC at which the message will be enqueued. This /// <summary>Gets or sets the date and time in UTC at which the message will be enqueued. This
@ -215,7 +271,7 @@ namespace Microsoft.Azure.ServiceBus
public long Size => Body.Length; public long Size => Body.Length;
/// <summary> /// <summary>
/// Gets the user property bag, which can be used for custom message properties. /// Gets the "user properties" bag, which can be used for custom message metadata.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Only following value types are supported: /// Only following value types are supported:
@ -237,8 +293,10 @@ namespace Microsoft.Azure.ServiceBus
return string.Format(CultureInfo.CurrentCulture, $"{{MessageId:{this.MessageId}}}"); return string.Format(CultureInfo.CurrentCulture, $"{{MessageId:{this.MessageId}}}");
} }
/// <summary>Clones a message, so that it is possible to send a clone of a message as a new message.</summary> /// <summary>Clones a message, so that it is possible to send a clone of an already received
/// <returns>The <see cref="Message" /> that contains the cloned message.</returns> /// message as a new message. The system properties of original message
/// are not copied.</summary>
/// <returns>A cloned <see cref="Message" />.</returns>
public Message Clone() public Message Clone()
{ {
var clone = (Message)this.MemberwiseClone(); var clone = (Message)this.MemberwiseClone();
@ -304,16 +362,25 @@ namespace Microsoft.Azure.ServiceBus
/// <summary> /// <summary>
/// Gets the lock token for the current message. /// Gets the lock token for the current message.
/// </summary> /// </summary>
/// <remarks>A lock token will only be specified if the message was received using <see cref="ReceiveMode.PeekLock"/></remarks> /// <remarks>
/// The lock token is a reference to the lock that is being held by the broker in <see cref="ReceiveMode.PeekLock"/> mode.
/// Locks are used to explicitly settle messages as explained in the <a href="https://docs.microsoft.com/azure/service-bus-messaging/message-transfers-locks-settlement">product documentation in more detail</a>.
/// The token can also be used to pin the lock permanently through the <a href="https://docs.microsoft.com/azure/service-bus-messaging/message-deferral">Deferral API</a> and, with that, take the message out of the
/// regular delivery state flow. This property is read-only.
/// </remarks>
public string LockToken => this.LockTokenGuid.ToString(); public string LockToken => this.LockTokenGuid.ToString();
/// <summary>Specifies if message is a received message or not.</summary> /// <summary>Specifies if the message has been obtained from the broker.</summary>
public bool IsReceived => this.sequenceNumber > -1; public bool IsReceived => this.sequenceNumber > -1;
/// <summary> /// <summary>
/// Get the current delivery count. /// Get the current delivery count.
/// </summary> /// </summary>
/// <value>This value starts at 1.</value> /// <value>This value starts at 1.</value>
/// <remarks>
/// Number of deliveries that have been attempted for this message. The count is incremented when a message lock expires,
/// or the message is explicitly abandoned by the receiver. This property is read-only.
/// </remarks>
public int DeliveryCount public int DeliveryCount
{ {
get get
@ -327,6 +394,11 @@ namespace Microsoft.Azure.ServiceBus
/// <summary>Gets the date and time in UTC until which the message will be locked in the queue/subscription.</summary> /// <summary>Gets the date and time in UTC until which the message will be locked in the queue/subscription.</summary>
/// <value>The date and time until which the message will be locked in the queue/subscription.</value> /// <value>The date and time until which the message will be locked in the queue/subscription.</value>
/// <remarks>
/// For messages retrieved under a lock (peek-lock receive mode, not pre-settled) this property reflects the UTC
/// instant until which the message is held locked in the queue/subscription. When the lock expires, the <see cref="DeliveryCount"/>
/// is incremented and the message is again available for retrieval. This property is read-only.
/// </remarks>
public DateTime LockedUntilUtc public DateTime LockedUntilUtc
{ {
get get
@ -338,7 +410,13 @@ namespace Microsoft.Azure.ServiceBus
internal set => this.lockedUntilUtc = value; internal set => this.lockedUntilUtc = value;
} }
/// <summary>Gets the unique number assigned to a message by Service Bus, for this entity.</summary> /// <summary>Gets the unique number assigned to a message by Service Bus.</summary>
/// <remarks>
/// The sequence number is a unique 64-bit integer assigned to a message as it is accepted
/// and stored by the broker and functions as its true identifier. For partitioned entities,
/// the topmost 16 bits reflect the partition identifier. Sequence numbers monotonically increase.
/// They roll over to 0 when the 48-64 bit range is exhausted. This property is read-only.
/// </remarks>
public long SequenceNumber public long SequenceNumber
{ {
get get
@ -353,6 +431,10 @@ namespace Microsoft.Azure.ServiceBus
/// <summary> /// <summary>
/// Gets the name of the queue or subscription that this message was enqueued on, before it was deadlettered. /// Gets the name of the queue or subscription that this message was enqueued on, before it was deadlettered.
/// </summary> /// </summary>
/// <remarks>
/// Only set in messages that have been dead-lettered and subsequently auto-forwarded from the dead-letter queue
/// to another entity. Indicates the entity in which the message was dead-lettered. This property is read-only.
/// </remarks>
public string DeadLetterSource public string DeadLetterSource
{ {
get get
@ -375,10 +457,12 @@ namespace Microsoft.Azure.ServiceBus
set => this.partitionId = value; set => this.partitionId = value;
} }
/// <summary>Gets or sets the enqueued sequence number of the message.</summary> /// <summary>Gets or sets the original sequence number of the message.</summary>
/// <value>The enqueued sequence number of the message.</value> /// <value>The enqueued sequence number of the message.</value>
/// <remarks>In scenarios of Topic-Subscription or ForwardTo, the message is initially enqueued on a different entity as compared to the /// <remarks>
/// entity from where the message is received. This returns the sequence number of the message in the initial entity.</remarks> /// For messages that have been auto-forwarded, this property reflects the sequence number
/// that had first been assigned to the message at its original point of submission. This property is read-only.
/// </remarks>
public long EnqueuedSequenceNumber public long EnqueuedSequenceNumber
{ {
get get
@ -391,7 +475,12 @@ namespace Microsoft.Azure.ServiceBus
} }
/// <summary>Gets or sets the date and time of the sent time in UTC.</summary> /// <summary>Gets or sets the date and time of the sent time in UTC.</summary>
/// <value>The enqueue time in UTC. This value represents the actual time of enqueuing the message.</value> /// <value>The enqueue time in UTC. </value>
/// <remarks>
/// The UTC instant at which the message has been accepted and stored in the entity.
/// This value can be used as an authoritative and neutral arrival time indicator when
/// the receiver does not want to trust the sender's clock. This property is read-only.
/// </remarks>
public DateTime EnqueuedTimeUtc public DateTime EnqueuedTimeUtc
{ {
get get

Просмотреть файл

@ -10,12 +10,12 @@ namespace Microsoft.Azure.ServiceBus
/// </summary> /// </summary>
public sealed class MessageLockLostException : ServiceBusException public sealed class MessageLockLostException : ServiceBusException
{ {
internal MessageLockLostException(string message) public MessageLockLostException(string message)
: this(message, null) : this(message, null)
{ {
} }
internal MessageLockLostException(string message, Exception innerException) public MessageLockLostException(string message, Exception innerException)
: base(false, message, innerException) : base(false, message, innerException)
{ {
} }

Просмотреть файл

@ -4,6 +4,7 @@
namespace Microsoft.Azure.ServiceBus namespace Microsoft.Azure.ServiceBus
{ {
using System; using System;
using System.Diagnostics;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Core; using Core;
@ -17,19 +18,21 @@ namespace Microsoft.Azure.ServiceBus
readonly IMessageReceiver messageReceiver; readonly IMessageReceiver messageReceiver;
readonly CancellationToken pumpCancellationToken; readonly CancellationToken pumpCancellationToken;
readonly SemaphoreSlim maxConcurrentCallsSemaphoreSlim; readonly SemaphoreSlim maxConcurrentCallsSemaphoreSlim;
readonly ServiceBusDiagnosticSource diagnosticSource;
public MessageReceivePump(IMessageReceiver messageReceiver, public MessageReceivePump(IMessageReceiver messageReceiver,
MessageHandlerOptions registerHandlerOptions, MessageHandlerOptions registerHandlerOptions,
Func<Message, CancellationToken, Task> callback, Func<Message, CancellationToken, Task> callback,
string endpoint, Uri endpoint,
CancellationToken pumpCancellationToken) CancellationToken pumpCancellationToken)
{ {
this.messageReceiver = messageReceiver ?? throw new ArgumentNullException(nameof(messageReceiver)); this.messageReceiver = messageReceiver ?? throw new ArgumentNullException(nameof(messageReceiver));
this.registerHandlerOptions = registerHandlerOptions; this.registerHandlerOptions = registerHandlerOptions;
this.onMessageCallback = callback; this.onMessageCallback = callback;
this.endpoint = endpoint; this.endpoint = endpoint.Authority;
this.pumpCancellationToken = pumpCancellationToken; this.pumpCancellationToken = pumpCancellationToken;
this.maxConcurrentCallsSemaphoreSlim = new SemaphoreSlim(this.registerHandlerOptions.MaxConcurrentCalls); this.maxConcurrentCallsSemaphoreSlim = new SemaphoreSlim(this.registerHandlerOptions.MaxConcurrentCalls);
this.diagnosticSource = new ServiceBusDiagnosticSource(messageReceiver.Path, endpoint);
} }
public void StartPump() public void StartPump()
@ -63,13 +66,28 @@ namespace Microsoft.Azure.ServiceBus
if (message != null) if (message != null)
{ {
MessagingEventSource.Log.MessageReceiverPumpTaskStart(this.messageReceiver.ClientId, message, this.maxConcurrentCallsSemaphoreSlim.CurrentCount); MessagingEventSource.Log.MessageReceiverPumpTaskStart(this.messageReceiver.ClientId, message, this.maxConcurrentCallsSemaphoreSlim.CurrentCount);
TaskExtensionHelper.Schedule(() => this.MessageDispatchTask(message));
TaskExtensionHelper.Schedule(() =>
{
if (ServiceBusDiagnosticSource.IsEnabled())
{
return this.MessageDispatchTaskInstrumented(message);
}
else
{
return this.MessageDispatchTask(message);
}
});
} }
} }
catch (Exception exception) catch (Exception exception)
{ {
MessagingEventSource.Log.MessageReceivePumpTaskException(this.messageReceiver.ClientId, string.Empty, exception); // Not reporting an ObjectDisposedException as we're stopping the pump
await this.RaiseExceptionReceived(exception, ExceptionReceivedEventArgsAction.Receive).ConfigureAwait(false); if (!(exception is ObjectDisposedException && this.pumpCancellationToken.IsCancellationRequested))
{
MessagingEventSource.Log.MessageReceivePumpTaskException(this.messageReceiver.ClientId, string.Empty, exception);
await this.RaiseExceptionReceived(exception, ExceptionReceivedEventArgsAction.Receive).ConfigureAwait(false);
}
} }
finally finally
{ {
@ -83,6 +101,26 @@ namespace Microsoft.Azure.ServiceBus
} }
} }
async Task MessageDispatchTaskInstrumented(Message message)
{
Activity activity = this.diagnosticSource.ProcessStart(message);
Task processTask = null;
try
{
processTask = MessageDispatchTask(message);
await processTask.ConfigureAwait(false);
}
catch (Exception e)
{
this.diagnosticSource.ReportException(e);
throw;
}
finally
{
this.diagnosticSource.ProcessStop(activity, message, processTask?.Status);
}
}
async Task MessageDispatchTask(Message message) async Task MessageDispatchTask(Message message)
{ {
CancellationTokenSource renewLockCancellationTokenSource = null; CancellationTokenSource renewLockCancellationTokenSource = null;
@ -103,6 +141,7 @@ namespace Microsoft.Azure.ServiceBus
{ {
MessagingEventSource.Log.MessageReceiverPumpUserCallbackStart(this.messageReceiver.ClientId, message); MessagingEventSource.Log.MessageReceiverPumpUserCallbackStart(this.messageReceiver.ClientId, message);
await this.onMessageCallback(message, this.pumpCancellationToken).ConfigureAwait(false); await this.onMessageCallback(message, this.pumpCancellationToken).ConfigureAwait(false);
MessagingEventSource.Log.MessageReceiverPumpUserCallbackStop(this.messageReceiver.ClientId, message); MessagingEventSource.Log.MessageReceiverPumpUserCallbackStop(this.messageReceiver.ClientId, message);
} }
catch (Exception exception) catch (Exception exception)
@ -116,8 +155,13 @@ namespace Microsoft.Azure.ServiceBus
await this.AbandonMessageIfNeededAsync(message).ConfigureAwait(false); await this.AbandonMessageIfNeededAsync(message).ConfigureAwait(false);
} }
if (ServiceBusDiagnosticSource.IsEnabled())
{
this.diagnosticSource.ReportException(exception);
}
// AbandonMessageIfNeededAsync should take care of not throwing exception // AbandonMessageIfNeededAsync should take care of not throwing exception
this.maxConcurrentCallsSemaphoreSlim.Release(); this.maxConcurrentCallsSemaphoreSlim.Release();
return; return;
} }
finally finally
@ -204,9 +248,10 @@ namespace Microsoft.Azure.ServiceBus
{ {
MessagingEventSource.Log.MessageReceiverPumpRenewMessageException(this.messageReceiver.ClientId, message, exception); MessagingEventSource.Log.MessageReceiverPumpRenewMessageException(this.messageReceiver.ClientId, message, exception);
// TaskCancelled is expected here as renewTasks will be cancelled after the Complete call is made. // TaskCanceled is expected here as renewTasks will be cancelled after the Complete call is made.
// Lets not bother user with this exception. // ObjectDisposedException should only happen here because the CancellationToken was disposed at which point
if (!(exception is TaskCanceledException)) // this renew exception is not relevant anymore. Lets not bother user with this exception.
if (!(exception is TaskCanceledException) && !(exception is ObjectDisposedException))
{ {
await this.RaiseExceptionReceived(exception, ExceptionReceivedEventArgsAction.RenewLock).ConfigureAwait(false); await this.RaiseExceptionReceived(exception, ExceptionReceivedEventArgsAction.RenewLock).ConfigureAwait(false);
} }

Просмотреть файл

@ -4,6 +4,7 @@
namespace Microsoft.Azure.ServiceBus namespace Microsoft.Azure.ServiceBus
{ {
using System; using System;
using System.Diagnostics;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Amqp; using Amqp;
@ -13,6 +14,8 @@ namespace Microsoft.Azure.ServiceBus
internal class MessageSession : MessageReceiver, IMessageSession internal class MessageSession : MessageReceiver, IMessageSession
{ {
private readonly ServiceBusDiagnosticSource diagnosticSource;
public MessageSession( public MessageSession(
string entityPath, string entityPath,
MessagingEntityType? entityType, MessagingEntityType? entityType,
@ -25,6 +28,7 @@ namespace Microsoft.Azure.ServiceBus
bool isSessionReceiver = false) bool isSessionReceiver = false)
: base(entityPath, entityType, receiveMode, serviceBusConnection, cbsTokenProvider, retryPolicy, prefetchCount, sessionId, isSessionReceiver) : base(entityPath, entityType, receiveMode, serviceBusConnection, cbsTokenProvider, retryPolicy, prefetchCount, sessionId, isSessionReceiver)
{ {
this.diagnosticSource = new ServiceBusDiagnosticSource(entityPath, serviceBusConnection.Endpoint);
} }
/// <summary> /// <summary>
@ -44,19 +48,19 @@ namespace Microsoft.Azure.ServiceBus
public Task<byte[]> GetStateAsync() public Task<byte[]> GetStateAsync()
{ {
this.ThrowIfClosed(); this.ThrowIfClosed();
return this.OnGetStateAsync(); return ServiceBusDiagnosticSource.IsEnabled() ? this.OnGetStateInstrumentedAsync() : this.OnGetStateAsync();
} }
public Task SetStateAsync(byte[] sessionState) public Task SetStateAsync(byte[] sessionState)
{ {
this.ThrowIfClosed(); this.ThrowIfClosed();
return this.OnSetStateAsync(sessionState); return ServiceBusDiagnosticSource.IsEnabled() ? this.OnSetStateInstrumentedAsync(sessionState) : this.OnSetStateAsync(sessionState);
} }
public Task RenewSessionLockAsync() public Task RenewSessionLockAsync()
{ {
this.ThrowIfClosed(); this.ThrowIfClosed();
return this.OnRenewSessionLockAsync(); return ServiceBusDiagnosticSource.IsEnabled() ? this.OnRenewSessionLockInstrumentedAsync() : this.OnRenewSessionLockAsync();
} }
protected override void OnMessageHandler(MessageHandlerOptions registerHandlerOptions, Func<Message, CancellationToken, Task> callback) protected override void OnMessageHandler(MessageHandlerOptions registerHandlerOptions, Func<Message, CancellationToken, Task> callback)
@ -162,5 +166,71 @@ namespace Microsoft.Azure.ServiceBus
throw new ObjectDisposedException($"MessageSession with Id '{this.ClientId}' has already been closed. Please accept a new MessageSession."); throw new ObjectDisposedException($"MessageSession with Id '{this.ClientId}' has already been closed. Please accept a new MessageSession.");
} }
} }
private async Task<byte[]> OnGetStateInstrumentedAsync()
{
Activity activity = this.diagnosticSource.GetSessionStateStart(this.SessionId);
Task<byte[]> getStateTask = null;
byte[] state = null;
try
{
getStateTask = this.OnGetStateAsync();
state = await getStateTask.ConfigureAwait(false);
return state;
}
catch (Exception ex)
{
this.diagnosticSource.ReportException(ex);
throw;
}
finally
{
this.diagnosticSource.GetSessionStateStop(activity, this.SessionId, state, getStateTask?.Status);
}
}
private async Task OnSetStateInstrumentedAsync(byte[] sessionState)
{
Activity activity = this.diagnosticSource.SetSessionStateStart(this.SessionId, sessionState);
Task setStateTask = null;
try
{
setStateTask = this.OnSetStateAsync(sessionState);
await setStateTask.ConfigureAwait(false);
}
catch (Exception ex)
{
this.diagnosticSource.ReportException(ex);
throw;
}
finally
{
this.diagnosticSource.SetSessionStateStop(activity, sessionState, this.SessionId, setStateTask?.Status);
}
}
private async Task OnRenewSessionLockInstrumentedAsync()
{
Activity activity = this.diagnosticSource.RenewSessionLockStart(this.SessionId);
Task renewTask = null;
try
{
renewTask = this.OnRenewSessionLockAsync();
await renewTask.ConfigureAwait(false);
}
catch (Exception ex)
{
this.diagnosticSource.ReportException(ex);
throw;
}
finally
{
this.diagnosticSource.RenewSessionLockStop(activity, this.SessionId, renewTask?.Status);
}
}
} }
} }

Просмотреть файл

@ -10,12 +10,12 @@ namespace Microsoft.Azure.ServiceBus
/// </summary> /// </summary>
public sealed class MessagingEntityDisabledException : ServiceBusException public sealed class MessagingEntityDisabledException : ServiceBusException
{ {
internal MessagingEntityDisabledException(string message) public MessagingEntityDisabledException(string message)
: this(message, null) : this(message, null)
{ {
} }
internal MessagingEntityDisabledException(string message, Exception innerException) public MessagingEntityDisabledException(string message, Exception innerException)
: base(false, message, innerException) : base(false, message, innerException)
{ {
} }

Просмотреть файл

@ -10,12 +10,12 @@ namespace Microsoft.Azure.ServiceBus
/// </summary> /// </summary>
public sealed class MessagingEntityNotFoundException : ServiceBusException public sealed class MessagingEntityNotFoundException : ServiceBusException
{ {
internal MessagingEntityNotFoundException(string message) public MessagingEntityNotFoundException(string message)
: this(message, null) : this(message, null)
{ {
} }
internal MessagingEntityNotFoundException(string message, Exception innerException) public MessagingEntityNotFoundException(string message, Exception innerException)
: base(false, message, innerException) : base(false, message, innerException)
{ {
} }

Просмотреть файл

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<Description>This is the next generation Azure Service Bus .NET Standard client library that focuses on queues &amp; topics. For more information about Service Bus, see https://azure.microsoft.com/en-us/services/service-bus/</Description> <Description>This is the next generation Azure Service Bus .NET Standard client library that focuses on queues &amp; topics. For more information about Service Bus, see https://azure.microsoft.com/en-us/services/service-bus/</Description>
<VersionPrefix>2.0.0</VersionPrefix> <VersionPrefix>3.0.0-preview-01</VersionPrefix>
<Authors>Microsoft</Authors> <Authors>Microsoft</Authors>
<TargetFrameworks>net461;netstandard2.0</TargetFrameworks> <TargetFrameworks>net461;netstandard2.0</TargetFrameworks>
<AssemblyOriginatorKeyFile>../../build/keyfile.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>../../build/keyfile.snk</AssemblyOriginatorKeyFile>
@ -31,6 +31,10 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Azure.Amqp" Version="2.1.2" /> <PackageReference Include="Microsoft.Azure.Amqp" Version="2.1.2" />
<PackageReference Include="Microsoft.Azure.Services.AppAuthentication" Version="1.1.0-preview" />
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="3.17.2" />
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="4.4.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="5.2.0-preview2-41113220915" />
<PackageReference Include="System.Runtime.Serialization.Xml" Version="4.3.0" /> <PackageReference Include="System.Runtime.Serialization.Xml" Version="4.3.0" />
</ItemGroup> </ItemGroup>

Просмотреть файл

@ -0,0 +1,96 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.Azure.ServiceBus.Primitives
{
using System;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
/// <summary>
/// Represents the Azure Active Directory token provider for the Service Bus.
/// </summary>
public class AzureActiveDirectoryTokenProvider : TokenProvider
{
readonly AuthenticationContext authContext;
readonly ClientCredential clientCredential;
#if !UAP10_0
readonly ClientAssertionCertificate clientAssertionCertificate;
#endif
readonly string clientId;
readonly Uri redirectUri;
readonly IPlatformParameters platformParameters;
readonly UserIdentifier userIdentifier;
enum AuthType
{
ClientCredential,
UserPasswordCredential,
ClientAssertionCertificate,
InteractiveUserLogin
}
readonly AuthType authType;
internal AzureActiveDirectoryTokenProvider(AuthenticationContext authContext, ClientCredential credential)
{
this.clientCredential = credential;
this.authContext = authContext;
this.authType = AuthType.ClientCredential;
this.clientId = clientCredential.ClientId;
}
#if !UAP10_0
internal AzureActiveDirectoryTokenProvider(AuthenticationContext authContext, ClientAssertionCertificate clientAssertionCertificate)
{
this.clientAssertionCertificate = clientAssertionCertificate;
this.authContext = authContext;
this.authType = AuthType.ClientAssertionCertificate;
this.clientId = clientAssertionCertificate.ClientId;
}
#endif
internal AzureActiveDirectoryTokenProvider(AuthenticationContext authContext, string clientId, Uri redirectUri, IPlatformParameters platformParameters, UserIdentifier userIdentifier)
{
this.authContext = authContext;
this.clientId = clientId;
this.redirectUri = redirectUri;
this.platformParameters = platformParameters;
this.userIdentifier = userIdentifier;
this.authType = AuthType.InteractiveUserLogin;
}
/// <summary>
/// Gets a <see cref="SecurityToken"/> for the given audience and duration.
/// </summary>
/// <param name="appliesTo">The URI which the access token applies to</param>
/// <param name="timeout">The time span that specifies the timeout value for the message that gets the security token</param>
/// <returns><see cref="SecurityToken"/></returns>
public override async Task<SecurityToken> GetTokenAsync(string appliesTo, TimeSpan timeout)
{
AuthenticationResult authResult;
switch (this.authType)
{
case AuthType.ClientCredential:
authResult = await this.authContext.AcquireTokenAsync(Constants.AadServiceBusAudience, this.clientCredential);
break;
#if !UAP10_0
case AuthType.ClientAssertionCertificate:
authResult = await this.authContext.AcquireTokenAsync(Constants.AadServiceBusAudience, this.clientAssertionCertificate);
break;
#endif
case AuthType.InteractiveUserLogin:
authResult = await this.authContext.AcquireTokenAsync(Constants.AadServiceBusAudience, this.clientId, this.redirectUri, this.platformParameters, this.userIdentifier);
break;
default:
throw new NotSupportedException();
}
return new JsonSecurityToken(authResult.AccessToken, appliesTo);
}
}
}

Просмотреть файл

@ -0,0 +1,25 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.Azure.ServiceBus.Primitives
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/// <summary>
/// Provides interface definition of a token provider.
/// </summary>
public interface ITokenProvider
{
/// <summary>
/// Gets a <see cref="SecurityToken"/>.
/// </summary>
/// <param name="appliesTo">The URI which the access token applies to</param>
/// <param name="timeout">The time span that specifies the timeout value for the message that gets the security token</param>
/// <returns><see cref="SecurityToken"/></returns>
Task<SecurityToken> GetTokenAsync(string appliesTo, TimeSpan timeout);
}
}

Просмотреть файл

@ -0,0 +1,32 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.Azure.ServiceBus.Primitives
{
using System;
using System.Collections.ObjectModel;
using System.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
/// <summary>
/// Extends SecurityToken for JWT specific properties
/// </summary>
public class JsonSecurityToken : SecurityToken
{
/// <summary>
/// Creates a new instance of the <see cref="JsonSecurityToken"/> class.
/// </summary>
/// <param name="rawToken">Raw JSON Web Token string</param>
/// <param name="audience">The audience</param>
public JsonSecurityToken(string rawToken, string audience)
: base(rawToken, GetExpirationDateTimeUtcFromToken(rawToken), audience, Constants.JsonWebTokenType)
{
}
static DateTime GetExpirationDateTimeUtcFromToken(string token)
{
var jwtSecurityToken = new JwtSecurityToken(token);
return jwtSecurityToken.ValidTo;
}
}
}

Просмотреть файл

@ -0,0 +1,29 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.Azure.ServiceBus.Primitives
{
using System;
using System.Threading.Tasks;
using Azure.Services.AppAuthentication;
/// <summary>
/// Represents the Azure Active Directory token provider for Azure Managed Service Identity integration.
/// </summary>
public class ManagedServiceIdentityTokenProvider : TokenProvider
{
static AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider();
/// <summary>
/// Gets a <see cref="SecurityToken"/> for the given audience and duration.
/// </summary>
/// <param name="appliesTo">The URI which the access token applies to</param>
/// <param name="timeout">The time span that specifies the timeout value for the message that gets the security token</param>
/// <returns><see cref="SecurityToken"/></returns>
public async override Task<SecurityToken> GetTokenAsync(string appliesTo, TimeSpan timeout)
{
string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync(Constants.AadServiceBusAudience);
return new JsonSecurityToken(accessToken, appliesTo);
}
}
}

Просмотреть файл

@ -4,79 +4,55 @@
namespace Microsoft.Azure.ServiceBus.Primitives namespace Microsoft.Azure.ServiceBus.Primitives
{ {
using System; using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net;
/// <summary> /// <summary>
/// Provides information about a security token such as audience, expiry time, and the string token value. /// Provides information about a security token such as audience, expiry time, and the string token value.
/// </summary> /// </summary>
internal class SecurityToken public class SecurityToken
{ {
// per Simple Web Token draft specification /// <summary>
private const string TokenAudience = "Audience"; /// Token literal
private const string TokenExpiresOn = "ExpiresOn"; /// </summary>
private const string TokenIssuer = "Issuer"; string token;
private const string TokenDigest256 = "HMACSHA256";
const string InternalExpiresOnFieldName = "ExpiresOn"; /// <summary>
const string InternalAudienceFieldName = TokenAudience; /// Expiry date-time
const string InternalKeyValueSeparator = "="; /// </summary>
const string InternalPairSeparator = "&"; DateTime expiresAtUtc;
static readonly Func<string, string> Decoder = WebUtility.UrlDecode;
static readonly DateTime EpochTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); /// <summary>
readonly string token; /// Token audience
readonly DateTime expiresAtUtc; /// </summary>
readonly string audience; string audience;
/// <summary>
/// Token type
/// </summary>
string tokenType;
/// <summary> /// <summary>
/// Creates a new instance of the <see cref="SecurityToken"/> class. /// Creates a new instance of the <see cref="SecurityToken"/> class.
/// </summary> /// </summary>
/// <param name="tokenString">The token</param>
/// <param name="expiresAtUtc">The expiration time</param> /// <param name="expiresAtUtc">The expiration time</param>
public SecurityToken(string tokenString, DateTime expiresAtUtc, string audience) /// <param name="audience">The audience</param>
/// <param name="tokenType">The type of the token</param>
public SecurityToken(string tokenString, DateTime expiresAtUtc, string audience, string tokenType)
{ {
if (string.IsNullOrWhiteSpace(tokenString)) if (string.IsNullOrEmpty(tokenString))
{ {
throw Fx.Exception.ArgumentNull(nameof(tokenString)); throw Fx.Exception.ArgumentNullOrWhiteSpace(nameof(tokenString));
} }
if (string.IsNullOrWhiteSpace(audience))
if (string.IsNullOrEmpty(audience))
{ {
throw Fx.Exception.ArgumentNull(nameof(audience)); throw Fx.Exception.ArgumentNullOrWhiteSpace(nameof(audience));
} }
this.token = tokenString; this.token = tokenString;
this.expiresAtUtc = expiresAtUtc; this.expiresAtUtc = expiresAtUtc;
this.audience = audience; this.audience = audience;
} this.tokenType = tokenType;
/// <summary>
/// Creates a new instance of the <see cref="SecurityToken"/> class.
/// </summary>
/// <param name="expiresAtUtc">The expiration time</param>
public SecurityToken(string tokenString, DateTime expiresAtUtc)
{
if (string.IsNullOrWhiteSpace(tokenString))
{
throw Fx.Exception.ArgumentNull(nameof(tokenString));
}
this.token = tokenString;
this.expiresAtUtc = expiresAtUtc;
this.audience = this.GetAudienceFromToken(tokenString);
}
/// <summary>
/// Creates a new instance of the <see cref="SecurityToken"/> class.
/// </summary>
public SecurityToken(string tokenString)
{
if (string.IsNullOrWhiteSpace(tokenString))
{
throw Fx.Exception.ArgumentNull(nameof(tokenString));
}
this.token = tokenString;
this.GetExpirationDateAndAudienceFromToken(tokenString, out this.expiresAtUtc, out this.audience);
} }
/// <summary> /// <summary>
@ -92,59 +68,11 @@ namespace Microsoft.Azure.ServiceBus.Primitives
/// <summary> /// <summary>
/// Gets the actual token. /// Gets the actual token.
/// </summary> /// </summary>
public object TokenValue => this.token; public virtual string TokenValue => this.token;
protected virtual string ExpiresOnFieldName => InternalExpiresOnFieldName; /// <summary>
/// Gets the token type.
protected virtual string AudienceFieldName => InternalAudienceFieldName; /// </summary>
public virtual string TokenType => this.tokenType;
protected virtual string KeyValueSeparator => InternalKeyValueSeparator;
protected virtual string PairSeparator => InternalPairSeparator;
static IDictionary<string, string> Decode(string encodedString, Func<string, string> keyDecoder, Func<string, string> valueDecoder, string keyValueSeparator, string pairSeparator)
{
var dictionary = new Dictionary<string, string>();
IEnumerable<string> valueEncodedPairs = encodedString.Split(new[] { pairSeparator }, StringSplitOptions.None);
foreach (var valueEncodedPairAsString in valueEncodedPairs)
{
var pair = valueEncodedPairAsString.Split(new[] { keyValueSeparator }, StringSplitOptions.None);
if (pair.Length != 2)
{
throw new FormatException(Resources.InvalidEncoding);
}
dictionary.Add(keyDecoder(pair[0]), valueDecoder(pair[1]));
}
return dictionary;
}
string GetAudienceFromToken(string token)
{
var decodedToken = Decode(token, Decoder, Decoder, this.KeyValueSeparator, this.PairSeparator);
if (!decodedToken.TryGetValue(this.AudienceFieldName, out var audience))
{
throw new FormatException(Resources.TokenMissingAudience);
}
return audience;
}
void GetExpirationDateAndAudienceFromToken(string token, out DateTime expiresOn, out string audience)
{
IDictionary<string, string> decodedToken = Decode(token, Decoder, Decoder, this.KeyValueSeparator, this.PairSeparator);
if (!decodedToken.TryGetValue(this.ExpiresOnFieldName, out var expiresIn))
{
throw new FormatException(Resources.TokenMissingExpiresOn);
}
if (!decodedToken.TryGetValue(this.AudienceFieldName, out audience))
{
throw new FormatException(Resources.TokenMissingAudience);
}
expiresOn = (EpochTime + TimeSpan.FromSeconds(double.Parse(expiresIn, CultureInfo.InvariantCulture)));
}
} }
} }

Просмотреть файл

@ -28,5 +28,22 @@ namespace Microsoft.Azure.ServiceBus.Primitives
this.InitializeConnection(serviceBusConnectionStringBuilder); this.InitializeConnection(serviceBusConnectionStringBuilder);
} }
public ServiceBusNamespaceConnection(string endpoint, TransportType transportType, RetryPolicy retryPolicy)
: base(Constants.DefaultOperationTimeout, retryPolicy)
{
if (string.IsNullOrWhiteSpace(endpoint))
{
throw Fx.Exception.ArgumentNullOrWhiteSpace(nameof(endpoint));
}
var serviceBusConnectionStringBuilder = new ServiceBusConnectionStringBuilder()
{
Endpoint = endpoint,
TransportType = transportType
};
this.InitializeConnection(serviceBusConnectionStringBuilder);
}
} }
} }

Просмотреть файл

@ -0,0 +1,150 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.Azure.ServiceBus.Primitives
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net;
/// <summary>
/// A WCF SecurityToken that wraps a Shared Access Signature
/// </summary>
class SharedAccessSignatureToken : SecurityToken
{
internal const string SharedAccessSignature = "SharedAccessSignature";
internal const string SignedResource = "sr";
internal const string Signature = "sig";
internal const string SignedKeyName = "skn";
internal const string SignedExpiry = "se";
internal const int MaxKeyNameLength = 256;
internal const int MaxKeyLength = 256;
const string SignedResourceFullFieldName = SharedAccessSignature + " " + SignedResource;
const string SasPairSeparator = "&";
const string SasKeyValueSeparator = "=";
static readonly Func<string, string> Decoder = WebUtility.UrlDecode;
/// <summary>
/// Creates a new instance of the <see cref="SharedAccessSignatureToken"/> class.
/// </summary>
/// <param name="tokenString">The token</param>
public SharedAccessSignatureToken(string tokenString)
: base(tokenString, GetExpirationDateTimeUtcFromToken(tokenString), GetAudienceFromToken(tokenString), Constants.SasTokenType)
{
}
internal static void Validate(string sharedAccessSignature)
{
if (string.IsNullOrEmpty(sharedAccessSignature))
{
throw new ArgumentNullException(nameof(sharedAccessSignature));
}
IDictionary<string, string> parsedFields = ExtractFieldValues(sharedAccessSignature);
string signature;
if (!parsedFields.TryGetValue(Signature, out signature))
{
throw new ArgumentNullException(Signature);
}
string expiry;
if (!parsedFields.TryGetValue(SignedExpiry, out expiry))
{
throw new ArgumentNullException(SignedExpiry);
}
string keyName;
if (!parsedFields.TryGetValue(SignedKeyName, out keyName))
{
throw new ArgumentNullException(SignedKeyName);
}
string encodedAudience;
if (!parsedFields.TryGetValue(SignedResource, out encodedAudience))
{
throw new ArgumentNullException(SignedResource);
}
}
static IDictionary<string, string> ExtractFieldValues(string sharedAccessSignature)
{
string[] tokenLines = sharedAccessSignature.Split();
if (!string.Equals(tokenLines[0].Trim(), SharedAccessSignature, StringComparison.OrdinalIgnoreCase) || tokenLines.Length != 2)
{
throw new ArgumentNullException(nameof(sharedAccessSignature));
}
IDictionary<string, string> parsedFields = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
string[] tokenFields = tokenLines[1].Trim().Split(new[] { SasPairSeparator }, StringSplitOptions.None);
foreach (string tokenField in tokenFields)
{
if (tokenField != string.Empty)
{
string[] fieldParts = tokenField.Split(new[] { SasKeyValueSeparator }, StringSplitOptions.None);
if (string.Equals(fieldParts[0], SignedResource, StringComparison.OrdinalIgnoreCase))
{
// We need to preserve the casing of the escape characters in the audience,
// so defer decoding the URL until later.
parsedFields.Add(fieldParts[0], fieldParts[1]);
}
else
{
parsedFields.Add(fieldParts[0], WebUtility.UrlDecode(fieldParts[1]));
}
}
}
return parsedFields;
}
static string GetAudienceFromToken(string token)
{
string audience;
IDictionary<string, string> decodedToken = Decode(token, Decoder, Decoder, SasKeyValueSeparator, SasPairSeparator);
if (!decodedToken.TryGetValue(SignedResourceFullFieldName, out audience))
{
throw new FormatException(Resources.TokenMissingAudience);
}
return audience;
}
static DateTime GetExpirationDateTimeUtcFromToken(string token)
{
string expiresIn;
IDictionary<string, string> decodedToken = Decode(token, Decoder, Decoder, SasKeyValueSeparator, SasPairSeparator);
if (!decodedToken.TryGetValue(SignedExpiry, out expiresIn))
{
throw new FormatException(Resources.TokenMissingExpiresOn);
}
var expiresOn = (Constants.EpochTime + TimeSpan.FromSeconds(double.Parse(expiresIn, CultureInfo.InvariantCulture)));
return expiresOn;
}
static IDictionary<string, string> Decode(string encodedString, Func<string, string> keyDecoder, Func<string, string> valueDecoder, string keyValueSeparator, string pairSeparator)
{
IDictionary<string, string> dictionary = new Dictionary<string, string>();
IEnumerable<string> valueEncodedPairs = encodedString.Split(new[] { pairSeparator }, StringSplitOptions.None);
foreach (string valueEncodedPair in valueEncodedPairs)
{
string[] pair = valueEncodedPair.Split(new[] { keyValueSeparator }, StringSplitOptions.None);
if (pair.Length != 2)
{
throw new FormatException(Resources.InvalidEncoding);
}
dictionary.Add(keyDecoder(pair[0]), valueDecoder(pair[1]));
}
return dictionary;
}
}
}

Просмотреть файл

@ -15,36 +15,42 @@ namespace Microsoft.Azure.ServiceBus.Primitives
/// <summary> /// <summary>
/// The SharedAccessSignatureTokenProvider generates tokens using a shared access key or existing signature. /// The SharedAccessSignatureTokenProvider generates tokens using a shared access key or existing signature.
/// </summary> /// </summary>
internal class SharedAccessSignatureTokenProvider : TokenProvider public class SharedAccessSignatureTokenProvider : TokenProvider
{ {
/// <summary> const TokenScope DefaultTokenScope = TokenScope.Entity;
/// Represents 00:00:00 UTC Thursday 1, January 1970.
/// </summary> internal static readonly TimeSpan DefaultTokenTimeout = TimeSpan.FromMinutes(60);
public static readonly DateTime EpochTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
readonly byte[] encodedSharedAccessKey; readonly byte[] encodedSharedAccessKey;
readonly string keyName; readonly string keyName;
readonly TimeSpan tokenTimeToLive; readonly TimeSpan tokenTimeToLive;
readonly TokenScope tokenScope;
readonly string sharedAccessSignature; readonly string sharedAccessSignature;
internal static readonly Func<string, byte[]> MessagingTokenProviderKeyEncoder = Encoding.UTF8.GetBytes;
internal SharedAccessSignatureTokenProvider(string sharedAccessSignature) internal SharedAccessSignatureTokenProvider(string sharedAccessSignature)
: base(TokenScope.Entity)
{ {
SharedAccessSignatureToken.Validate(sharedAccessSignature); SharedAccessSignatureToken.Validate(sharedAccessSignature);
this.sharedAccessSignature = sharedAccessSignature; this.sharedAccessSignature = sharedAccessSignature;
} }
internal SharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey, TimeSpan tokenTimeToLive) internal SharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey, TokenScope tokenScope = TokenScope.Entity)
: this(keyName, sharedAccessKey, tokenTimeToLive, TokenScope.Entity) : this(keyName, sharedAccessKey, MessagingTokenProviderKeyEncoder, DefaultTokenTimeout, tokenScope)
{ {
} }
internal SharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey, TimeSpan tokenTimeToLive, TokenScope tokenScope) internal SharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey, TimeSpan tokenTimeToLive, TokenScope tokenScope = TokenScope.Entity)
: this(keyName, sharedAccessKey, TokenProvider.MessagingTokenProviderKeyEncoder, tokenTimeToLive, tokenScope) : this(keyName, sharedAccessKey, MessagingTokenProviderKeyEncoder, tokenTimeToLive, tokenScope)
{ {
} }
/// <summary></summary>
/// <param name="keyName"></param>
/// <param name="sharedAccessKey"></param>
/// <param name="customKeyEncoder"></param>
/// <param name="tokenTimeToLive"></param>
/// <param name="tokenScope"></param>
protected SharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey, Func<string, byte[]> customKeyEncoder, TimeSpan tokenTimeToLive, TokenScope tokenScope) protected SharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey, Func<string, byte[]> customKeyEncoder, TimeSpan tokenTimeToLive, TokenScope tokenScope)
: base(tokenScope)
{ {
if (string.IsNullOrEmpty(keyName)) if (string.IsNullOrEmpty(keyName))
{ {
@ -74,16 +80,28 @@ namespace Microsoft.Azure.ServiceBus.Primitives
this.tokenTimeToLive = tokenTimeToLive; this.tokenTimeToLive = tokenTimeToLive;
this.encodedSharedAccessKey = customKeyEncoder != null ? this.encodedSharedAccessKey = customKeyEncoder != null ?
customKeyEncoder(sharedAccessKey) : customKeyEncoder(sharedAccessKey) :
TokenProvider.MessagingTokenProviderKeyEncoder(sharedAccessKey); MessagingTokenProviderKeyEncoder(sharedAccessKey);
this.tokenScope = tokenScope;
} }
protected override Task<SecurityToken> OnGetTokenAsync(string appliesTo, string action, TimeSpan timeout) /// <summary>
/// Gets a <see cref="SecurityToken"/> for the given audience and duration.
/// </summary>
/// <param name="appliesTo">The URI which the access token applies to</param>
/// <param name="timeout">The time span that specifies the timeout value for the message that gets the security token</param>
/// <returns><see cref="SecurityToken"/></returns>
public override Task<SecurityToken> GetTokenAsync(string appliesTo, TimeSpan timeout)
{ {
var tokenString = this.BuildSignature(appliesTo); TimeoutHelper.ThrowIfNegativeArgument(timeout);
var sharedAccessSignatureToken = new SharedAccessSignatureToken(tokenString); appliesTo = NormalizeAppliesTo(appliesTo);
return Task.FromResult<SecurityToken>(sharedAccessSignatureToken); string tokenString = this.BuildSignature(appliesTo);
var securityToken = new SharedAccessSignatureToken(tokenString);
return Task.FromResult<SecurityToken>(securityToken);
} }
/// <summary></summary>
/// <param name="targetUri"></param>
/// <returns></returns>
protected virtual string BuildSignature(string targetUri) protected virtual string BuildSignature(string targetUri)
{ {
return string.IsNullOrWhiteSpace(this.sharedAccessSignature) return string.IsNullOrWhiteSpace(this.sharedAccessSignature)
@ -95,6 +113,11 @@ namespace Microsoft.Azure.ServiceBus.Primitives
: this.sharedAccessSignature; : this.sharedAccessSignature;
} }
string NormalizeAppliesTo(string appliesTo)
{
return ServiceBusUriHelper.NormalizeUri(appliesTo, "http", true, stripPath: this.tokenScope == TokenScope.Namespace, ensureTrailingSlash: true);
}
static class SharedAccessSignatureBuilder static class SharedAccessSignatureBuilder
{ {
[SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Uris are normalized to lowercase")] [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Uris are normalized to lowercase")]
@ -104,39 +127,34 @@ namespace Microsoft.Azure.ServiceBus.Primitives
string targetUri, string targetUri,
TimeSpan timeToLive) TimeSpan timeToLive)
{ {
// Note that target URI is not normalized because in IoT scenario it // Note that target URI is not normalized because in IoT scenario it
// is case sensitive. // is case sensitive.
string expiresOn = BuildExpiresOn(timeToLive); string expiresOn = BuildExpiresOn(timeToLive);
string audienceUri = WebUtility.UrlEncode(targetUri); string audienceUri = WebUtility.UrlEncode(targetUri);
var fields = new List<string> { audienceUri, expiresOn }; List<string> fields = new List<string> { audienceUri, expiresOn };
// Example string to be signed: // Example string to be signed:
// http://mynamespace.servicebus.windows.net/a/b/c?myvalue1=a // http://mynamespace.servicebus.windows.net/a/b/c?myvalue1=a
// <Value for ExpiresOn> // <Value for ExpiresOn>
var signature = Sign(string.Join("\n", fields), encodedSharedAccessKey); string signature = Sign(string.Join("\n", fields), encodedSharedAccessKey);
// Example returned string: // Example returned string:
// SharedAccessKeySignature // SharedAccessKeySignature
// sr=ENCODED(http://mynamespace.servicebus.windows.net/a/b/c?myvalue1=a)&sig=<Signature>&se=<ExpiresOnValue>&skn=<KeyName> // sr=ENCODED(http://mynamespace.servicebus.windows.net/a/b/c?myvalue1=a)&sig=<Signature>&se=<ExpiresOnValue>&skn=<KeyName>
return string.Format(
CultureInfo.InvariantCulture, return string.Format(CultureInfo.InvariantCulture, "{0} {1}={2}&{3}={4}&{5}={6}&{7}={8}",
"{0} {1}={2}&{3}={4}&{5}={6}&{7}={8}",
SharedAccessSignatureToken.SharedAccessSignature, SharedAccessSignatureToken.SharedAccessSignature,
SharedAccessSignatureToken.SignedResource, SharedAccessSignatureToken.SignedResource, audienceUri,
audienceUri, SharedAccessSignatureToken.Signature, WebUtility.UrlEncode(signature),
SharedAccessSignatureToken.Signature, SharedAccessSignatureToken.SignedExpiry, WebUtility.UrlEncode(expiresOn),
WebUtility.UrlEncode(signature), SharedAccessSignatureToken.SignedKeyName, WebUtility.UrlEncode(keyName));
SharedAccessSignatureToken.SignedExpiry,
WebUtility.UrlEncode(expiresOn),
SharedAccessSignatureToken.SignedKeyName,
WebUtility.UrlEncode(keyName));
} }
static string BuildExpiresOn(TimeSpan timeToLive) static string BuildExpiresOn(TimeSpan timeToLive)
{ {
var expiresOn = DateTime.UtcNow.Add(timeToLive); DateTime expiresOn = DateTime.UtcNow.Add(timeToLive);
var secondsFromBaseTime = expiresOn.Subtract(EpochTime); TimeSpan secondsFromBaseTime = expiresOn.Subtract(Constants.EpochTime);
var seconds = Convert.ToInt64(secondsFromBaseTime.TotalSeconds, CultureInfo.InvariantCulture); long seconds = Convert.ToInt64(secondsFromBaseTime.TotalSeconds, CultureInfo.InvariantCulture);
return Convert.ToString(seconds, CultureInfo.InvariantCulture); return Convert.ToString(seconds, CultureInfo.InvariantCulture);
} }
@ -148,98 +166,5 @@ namespace Microsoft.Azure.ServiceBus.Primitives
} }
} }
} }
/// <summary>
/// A WCF SecurityToken that wraps a Shared Access Signature
/// </summary>
class SharedAccessSignatureToken : SecurityToken
{
public const int MaxKeyNameLength = 256;
public const int MaxKeyLength = 256;
public const string SharedAccessSignature = "SharedAccessSignature";
public const string SignedResource = "sr";
public const string Signature = "sig";
public const string SignedKeyName = "skn";
public const string SignedExpiry = "se";
public const string SignedResourceFullFieldName = SharedAccessSignature + " " + SignedResource;
public const string SasKeyValueSeparator = "=";
public const string SasPairSeparator = "&";
public SharedAccessSignatureToken(string tokenString)
: base(tokenString)
{
}
protected override string AudienceFieldName => SignedResourceFullFieldName;
protected override string ExpiresOnFieldName => SignedExpiry;
protected override string KeyValueSeparator => SasKeyValueSeparator;
protected override string PairSeparator => SasPairSeparator;
internal static void Validate(string sharedAccessSignature)
{
if (string.IsNullOrEmpty(sharedAccessSignature))
{
throw new ArgumentNullException(nameof(sharedAccessSignature));
}
IDictionary<string, string> parsedFields = ExtractFieldValues(sharedAccessSignature);
if (!parsedFields.TryGetValue(Signature, out _))
{
throw new ArgumentNullException(Signature);
}
if (!parsedFields.TryGetValue(SignedExpiry, out _))
{
throw new ArgumentNullException(SignedExpiry);
}
if (!parsedFields.TryGetValue(SignedKeyName, out _))
{
throw new ArgumentNullException(SignedKeyName);
}
if (!parsedFields.TryGetValue(SignedResource, out _))
{
throw new ArgumentNullException(SignedResource);
}
}
static IDictionary<string, string> ExtractFieldValues(string sharedAccessSignature)
{
var tokenLines = sharedAccessSignature.Split();
if (!string.Equals(tokenLines[0].Trim(), SharedAccessSignature, StringComparison.OrdinalIgnoreCase) || tokenLines.Length != 2)
{
throw new ArgumentNullException(nameof(sharedAccessSignature));
}
var parsedFields = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var tokenFields = tokenLines[1].Trim().Split(new[] { SasPairSeparator }, StringSplitOptions.None);
foreach (var tokenField in tokenFields)
{
if (tokenField != string.Empty)
{
var fieldParts = tokenField.Split(new[] { SasKeyValueSeparator }, StringSplitOptions.None);
if (string.Equals(fieldParts[0], SignedResource, StringComparison.OrdinalIgnoreCase))
{
// We need to preserve the casing of the escape characters in the audience,
// so defer decoding the URL until later.
parsedFields.Add(fieldParts[0], fieldParts[1]);
}
else
{
parsedFields.Add(fieldParts[0], WebUtility.UrlDecode(fieldParts[1]));
}
}
}
return parsedFields;
}
}
} }
} }

Просмотреть файл

@ -4,36 +4,14 @@
namespace Microsoft.Azure.ServiceBus.Primitives namespace Microsoft.Azure.ServiceBus.Primitives
{ {
using System; using System;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
/// <summary> /// <summary>
/// This abstract base class can be extended to implement additional token providers. /// This abstract base class can be extended to implement additional token providers.
/// </summary> /// </summary>
internal abstract class TokenProvider public abstract class TokenProvider : ITokenProvider
{ {
internal static readonly TimeSpan DefaultTokenTimeout = TimeSpan.FromMinutes(60);
internal static readonly Func<string, byte[]> MessagingTokenProviderKeyEncoder = Encoding.UTF8.GetBytes;
const TokenScope DefaultTokenScope = TokenScope.Entity;
protected TokenProvider()
: this(TokenProvider.DefaultTokenScope)
{
}
protected TokenProvider(TokenScope tokenScope)
{
this.TokenScope = tokenScope;
this.ThisLock = new object();
}
/// <summary>
/// Gets the scope or permissions associated with the token.
/// </summary>
public TokenScope TokenScope { get; }
protected object ThisLock { get; }
/// <summary> /// <summary>
/// Construct a TokenProvider based on a sharedAccessSignature. /// Construct a TokenProvider based on a sharedAccessSignature.
/// </summary> /// </summary>
@ -52,19 +30,20 @@ namespace Microsoft.Azure.ServiceBus.Primitives
/// <returns>A TokenProvider initialized with the provided RuleId and Password</returns> /// <returns>A TokenProvider initialized with the provided RuleId and Password</returns>
public static TokenProvider CreateSharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey) public static TokenProvider CreateSharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey)
{ {
return new SharedAccessSignatureTokenProvider(keyName, sharedAccessKey, DefaultTokenTimeout); return new SharedAccessSignatureTokenProvider(keyName, sharedAccessKey);
} }
////internal static TokenProvider CreateIoTTokenProvider(string keyName, string sharedAccessKey) //internal static TokenProvider CreateIoTTokenProvider(string keyName, string sharedAccessKey)
////{ //{
//// return new IoTTokenProvider(keyName, sharedAccessKey, DefaultTokenTimeout); // return new IoTTokenProvider(keyName, sharedAccessKey, DefaultTokenTimeout);
////} //}
/// <summary> /// <summary>
/// Construct a TokenProvider based on the provided Key Name and Shared Access Key. /// Construct a TokenProvider based on the provided Key Name and Shared Access Key.
/// </summary> /// </summary>
/// <param name="keyName">The key name of the corresponding SharedAccessKeyAuthorizationRule.</param> /// <param name="keyName">The key name of the corresponding SharedAccessKeyAuthorizationRule.</param>
/// <param name="sharedAccessKey">The key associated with the SharedAccessKeyAuthorizationRule</param> /// <param name="sharedAccessKey">The key associated with the SharedAccessKeyAuthorizationRule</param>
/// <param name="tokenTimeToLive">The token time to live</param>
/// <returns>A TokenProvider initialized with the provided RuleId and Password</returns> /// <returns>A TokenProvider initialized with the provided RuleId and Password</returns>
public static TokenProvider CreateSharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey, TimeSpan tokenTimeToLive) public static TokenProvider CreateSharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey, TimeSpan tokenTimeToLive)
{ {
@ -80,7 +59,7 @@ namespace Microsoft.Azure.ServiceBus.Primitives
/// <returns>A TokenProvider initialized with the provided RuleId and Password</returns> /// <returns>A TokenProvider initialized with the provided RuleId and Password</returns>
public static TokenProvider CreateSharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey, TokenScope tokenScope) public static TokenProvider CreateSharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey, TokenScope tokenScope)
{ {
return new SharedAccessSignatureTokenProvider(keyName, sharedAccessKey, DefaultTokenTimeout, tokenScope); return new SharedAccessSignatureTokenProvider(keyName, sharedAccessKey, tokenScope);
} }
/// <summary> /// <summary>
@ -88,6 +67,7 @@ namespace Microsoft.Azure.ServiceBus.Primitives
/// </summary> /// </summary>
/// <param name="keyName">The key name of the corresponding SharedAccessKeyAuthorizationRule.</param> /// <param name="keyName">The key name of the corresponding SharedAccessKeyAuthorizationRule.</param>
/// <param name="sharedAccessKey">The key associated with the SharedAccessKeyAuthorizationRule</param> /// <param name="sharedAccessKey">The key associated with the SharedAccessKeyAuthorizationRule</param>
/// <param name="tokenTimeToLive">The token time to live</param>
/// <param name="tokenScope">The tokenScope of tokens to request.</param> /// <param name="tokenScope">The tokenScope of tokens to request.</param>
/// <returns>A TokenProvider initialized with the provided RuleId and Password</returns> /// <returns>A TokenProvider initialized with the provided RuleId and Password</returns>
public static TokenProvider CreateSharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey, TimeSpan tokenTimeToLive, TokenScope tokenScope) public static TokenProvider CreateSharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey, TimeSpan tokenTimeToLive, TokenScope tokenScope)
@ -95,23 +75,96 @@ namespace Microsoft.Azure.ServiceBus.Primitives
return new SharedAccessSignatureTokenProvider(keyName, sharedAccessKey, tokenTimeToLive, tokenScope); return new SharedAccessSignatureTokenProvider(keyName, sharedAccessKey, tokenTimeToLive, tokenScope);
} }
/// <summary>Creates an Azure Active Directory token provider.</summary>
/// <param name="authContext">AuthenticationContext for AAD.</param>
/// <param name="clientCredential">The app credential.</param>
/// <returns>The <see cref="TokenProvider" /> for returning Json web token.</returns>
public static TokenProvider CreateAadTokenProvider(AuthenticationContext authContext, ClientCredential clientCredential)
{
if (authContext == null)
{
throw new ArgumentNullException(nameof(authContext));
}
if (clientCredential == null)
{
throw new ArgumentNullException(nameof(clientCredential));
}
return new AzureActiveDirectoryTokenProvider(authContext, clientCredential);
}
/// <summary>Creates an Azure Active Directory token provider.</summary>
/// <param name="authContext">AuthenticationContext for AAD.</param>
/// <param name="clientId">ClientId for AAD.</param>
/// <param name="redirectUri">The redirectUri on Client App.</param>
/// <param name="platformParameters">Platform parameters</param>
/// <param name="userIdentifier">User Identifier</param>
/// <returns>The <see cref="TokenProvider" /> for returning Json web token.</returns>
public static TokenProvider CreateAadTokenProvider(
AuthenticationContext authContext,
string clientId,
Uri redirectUri,
IPlatformParameters platformParameters,
UserIdentifier userIdentifier = null)
{
if (authContext == null)
{
throw new ArgumentNullException(nameof(authContext));
}
if (string.IsNullOrEmpty(clientId))
{
throw new ArgumentNullException(nameof(clientId));
}
if (redirectUri == null)
{
throw new ArgumentNullException(nameof(redirectUri));
}
if (platformParameters == null)
{
throw new ArgumentNullException(nameof(platformParameters));
}
return new AzureActiveDirectoryTokenProvider(authContext, clientId, redirectUri, platformParameters, userIdentifier);
}
#if !UAP10_0
/// <summary>Creates an Azure Active Directory token provider.</summary>
/// <param name="authContext">AuthenticationContext for AAD.</param>
/// <param name="clientAssertionCertificate">The client assertion certificate credential.</param>
/// <returns>The <see cref="TokenProvider" /> for returning Json web token.</returns>
public static TokenProvider CreateAadTokenProvider(AuthenticationContext authContext, ClientAssertionCertificate clientAssertionCertificate)
{
if (authContext == null)
{
throw new ArgumentNullException(nameof(authContext));
}
if (clientAssertionCertificate == null)
{
throw new ArgumentNullException(nameof(clientAssertionCertificate));
}
return new AzureActiveDirectoryTokenProvider(authContext, clientAssertionCertificate);
}
#endif
/// <summary>Creates Azure Managed Service Identity token provider.</summary>
/// <returns>The <see cref="TokenProvider" /> for returning Json web token.</returns>
public static TokenProvider CreateManagedServiceIdentityTokenProvider()
{
return new ManagedServiceIdentityTokenProvider();
}
/// <summary> /// <summary>
/// Gets a <see cref="SecurityToken"/> for the given audience and duration. /// Gets a <see cref="SecurityToken"/> for the given audience and duration.
/// </summary> /// </summary>
/// <param name="appliesTo">The URI which the access token applies to</param> /// <param name="appliesTo">The URI which the access token applies to</param>
/// <param name="timeout">The time span that specifies the timeout value for the message that gets the security token</param> /// <param name="timeout">The time span that specifies the timeout value for the message that gets the security token</param>
public Task<SecurityToken> GetTokenAsync(string appliesTo, string action, TimeSpan timeout) /// <returns></returns>
{ public abstract Task<SecurityToken> GetTokenAsync(string appliesTo, TimeSpan timeout);
TimeoutHelper.ThrowIfNegativeArgument(timeout);
appliesTo = this.NormalizeAppliesTo(appliesTo);
return this.OnGetTokenAsync(appliesTo, action, timeout);
}
protected abstract Task<SecurityToken> OnGetTokenAsync(string appliesTo, string action, TimeSpan timeout);
protected virtual string NormalizeAppliesTo(string appliesTo)
{
return ServiceBusUriHelper.NormalizeUri(appliesTo, "http", true, stripPath: this.TokenScope == TokenScope.Namespace, ensureTrailingSlash: true);
}
} }
} }

Просмотреть файл

@ -14,10 +14,10 @@ namespace Microsoft.Azure.ServiceBus.Primitives
/// </summary> /// </summary>
sealed class TokenProviderAdapter : ICbsTokenProvider sealed class TokenProviderAdapter : ICbsTokenProvider
{ {
readonly TokenProvider tokenProvider; readonly ITokenProvider tokenProvider;
readonly TimeSpan operationTimeout; readonly TimeSpan operationTimeout;
public TokenProviderAdapter(TokenProvider tokenProvider, TimeSpan operationTimeout) public TokenProviderAdapter(ITokenProvider tokenProvider, TimeSpan operationTimeout)
{ {
Debug.Assert(tokenProvider != null, "tokenProvider cannot be null"); Debug.Assert(tokenProvider != null, "tokenProvider cannot be null");
this.tokenProvider = tokenProvider; this.tokenProvider = tokenProvider;
@ -27,7 +27,7 @@ namespace Microsoft.Azure.ServiceBus.Primitives
public async Task<CbsToken> GetTokenAsync(Uri namespaceAddress, string appliesTo, string[] requiredClaims) public async Task<CbsToken> GetTokenAsync(Uri namespaceAddress, string appliesTo, string[] requiredClaims)
{ {
var claim = requiredClaims?.FirstOrDefault(); var claim = requiredClaims?.FirstOrDefault();
var securityToken = await this.tokenProvider.GetTokenAsync(appliesTo, claim, this.operationTimeout).ConfigureAwait(false); var securityToken = await this.tokenProvider.GetTokenAsync(appliesTo, this.operationTimeout).ConfigureAwait(false);
return new CbsToken(securityToken.TokenValue, CbsConstants.ServiceBusSasTokenType, securityToken.ExpiresAtUtc); return new CbsToken(securityToken.TokenValue, CbsConstants.ServiceBusSasTokenType, securityToken.ExpiresAtUtc);
} }
} }

Просмотреть файл

@ -6,7 +6,7 @@ namespace Microsoft.Azure.ServiceBus.Primitives
/// <summary> /// <summary>
/// A enum representing the scope of the <see cref="SecurityToken"/>. /// A enum representing the scope of the <see cref="SecurityToken"/>.
/// </summary> /// </summary>
internal enum TokenScope public enum TokenScope
{ {
/// <summary> /// <summary>
/// The namespace. /// The namespace.

Просмотреть файл

@ -7,9 +7,9 @@ namespace Microsoft.Azure.ServiceBus
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Core; using Microsoft.Azure.Amqp;
using Azure.Amqp; using Microsoft.Azure.ServiceBus.Core;
using Primitives; using Microsoft.Azure.ServiceBus.Primitives;
/// <summary> /// <summary>
/// QueueClient can be used for all basic interactions with a Service Bus Queue. /// QueueClient can be used for all basic interactions with a Service Bus Queue.
@ -56,6 +56,7 @@ namespace Microsoft.Azure.ServiceBus
{ {
readonly bool ownsConnection; readonly bool ownsConnection;
readonly object syncLock; readonly object syncLock;
int prefetchCount; int prefetchCount;
MessageSender innerSender; MessageSender innerSender;
MessageReceiver innerReceiver; MessageReceiver innerReceiver;
@ -94,6 +95,37 @@ namespace Microsoft.Azure.ServiceBus
throw Fx.Exception.ArgumentNullOrWhiteSpace(entityPath); throw Fx.Exception.ArgumentNullOrWhiteSpace(entityPath);
} }
this.InternalTokenProvider = this.ServiceBusConnection.CreateTokenProvider();
this.CbsTokenProvider = new TokenProviderAdapter(this.InternalTokenProvider, this.ServiceBusConnection.OperationTimeout);
this.ownsConnection = true;
}
/// <summary>
/// Creates a new instance of the Queue client using the specified endpoint, entity path, and token provider.
/// </summary>
/// <param name="endpoint">Fully qualified domain name for Service Bus. Most likely, {yournamespace}.servicebus.windows.net</param>
/// <param name="entityPath">Queue path.</param>
/// <param name="tokenProvider">Token provider which will generate security tokens for authorization.</param>
/// <param name="transportType">Transport type.</param>
/// <param name="receiveMode">Mode of receive of messages. Defaults to <see cref="ReceiveMode"/>.PeekLock.</param>
/// <param name="retryPolicy">Retry policy for queue operations. Defaults to <see cref="RetryPolicy.Default"/></param>
/// <returns></returns>
public QueueClient(
string endpoint,
string entityPath,
ITokenProvider tokenProvider,
TransportType transportType = TransportType.Amqp,
ReceiveMode receiveMode = ReceiveMode.PeekLock,
RetryPolicy retryPolicy = null)
: this(new ServiceBusNamespaceConnection(endpoint, transportType, retryPolicy), entityPath, receiveMode, retryPolicy)
{
if (tokenProvider == null)
{
throw Fx.Exception.ArgumentNull(nameof(tokenProvider));
}
this.InternalTokenProvider = tokenProvider;
this.CbsTokenProvider = new TokenProviderAdapter(this.InternalTokenProvider, this.ServiceBusConnection.OperationTimeout);
this.ownsConnection = true; this.ownsConnection = true;
} }
@ -107,8 +139,6 @@ namespace Microsoft.Azure.ServiceBus
this.syncLock = new object(); this.syncLock = new object();
this.QueueName = entityPath; this.QueueName = entityPath;
this.ReceiveMode = receiveMode; this.ReceiveMode = receiveMode;
this.TokenProvider = this.ServiceBusConnection.CreateTokenProvider();
this.CbsTokenProvider = new TokenProviderAdapter(this.TokenProvider, serviceBusConnection.OperationTimeout);
MessagingEventSource.Log.QueueClientCreateStop(serviceBusConnection.Endpoint.Authority, entityPath, this.ClientId); MessagingEventSource.Log.QueueClientCreateStop(serviceBusConnection.Endpoint.Authority, entityPath, this.ClientId);
} }
@ -276,7 +306,7 @@ namespace Microsoft.Azure.ServiceBus
this.ClientId, this.ClientId,
this.ReceiveMode, this.ReceiveMode,
this.SessionClient, this.SessionClient,
this.ServiceBusConnection.Endpoint.Authority); this.ServiceBusConnection.Endpoint);
} }
} }
} }
@ -289,7 +319,7 @@ namespace Microsoft.Azure.ServiceBus
ICbsTokenProvider CbsTokenProvider { get; } ICbsTokenProvider CbsTokenProvider { get; }
TokenProvider TokenProvider { get; } ITokenProvider InternalTokenProvider { get; }
/// <summary> /// <summary>
/// Sends a message to Service Bus. /// Sends a message to Service Bus.
@ -305,6 +335,7 @@ namespace Microsoft.Azure.ServiceBus
public Task SendAsync(IList<Message> messageList) public Task SendAsync(IList<Message> messageList)
{ {
this.ThrowIfClosed(); this.ThrowIfClosed();
return this.InnerSender.SendAsync(messageList); return this.InnerSender.SendAsync(messageList);
} }

Просмотреть файл

@ -11,12 +11,12 @@ namespace Microsoft.Azure.ServiceBus
/// </summary> /// </summary>
public sealed class QuotaExceededException : ServiceBusException public sealed class QuotaExceededException : ServiceBusException
{ {
internal QuotaExceededException(string message) public QuotaExceededException(string message)
: this(message, null) : this(message, null)
{ {
} }
internal QuotaExceededException(string message, Exception innerException) public QuotaExceededException(string message, Exception innerException)
: base(false, message, innerException) : base(false, message, innerException)
{ {
} }

Просмотреть файл

@ -10,12 +10,12 @@ namespace Microsoft.Azure.ServiceBus
/// </summary> /// </summary>
public sealed class ServerBusyException : ServiceBusException public sealed class ServerBusyException : ServiceBusException
{ {
internal ServerBusyException(string message) public ServerBusyException(string message)
: this(message, null) : this(message, null)
{ {
} }
internal ServerBusyException(string message, Exception innerException) public ServerBusyException(string message, Exception innerException)
: base(true, message, innerException) : base(true, message, innerException)
{ {
} }

Просмотреть файл

@ -10,12 +10,12 @@ namespace Microsoft.Azure.ServiceBus
/// </summary> /// </summary>
public class ServiceBusCommunicationException : ServiceBusException public class ServiceBusCommunicationException : ServiceBusException
{ {
protected internal ServiceBusCommunicationException(string message) public ServiceBusCommunicationException(string message)
: this(message, null) : this(message, null)
{ {
} }
protected internal ServiceBusCommunicationException(string message, Exception innerException) public ServiceBusCommunicationException(string message, Exception innerException)
: base(true, message, innerException) : base(true, message, innerException)
{ {
} }

Просмотреть файл

@ -0,0 +1,773 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.Azure.ServiceBus
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus.Diagnostics;
internal class ServiceBusDiagnosticSource
{
public const string DiagnosticListenerName = "Microsoft.Azure.ServiceBus";
public const string BaseActivityName = "Microsoft.Azure.ServiceBus.";
public const string ExceptionEventName = BaseActivityName + "Exception";
public const string ProcessActivityName = BaseActivityName + "Process";
public const string ActivityIdPropertyName = "Diagnostic-Id";
public const string CorrelationContextPropertyName = "Correlation-Context";
public const string RelatedToTag = "RelatedTo";
public const string MessageIdTag = "MessageId";
public const string SessionIdTag = "SessionId";
private static readonly DiagnosticListener DiagnosticListener = new DiagnosticListener(DiagnosticListenerName);
private readonly string entityPath;
private readonly Uri endpoint;
public ServiceBusDiagnosticSource(string entityPath, Uri endpoint)
{
this.entityPath = entityPath;
this.endpoint = endpoint;
}
public static bool IsEnabled()
{
return DiagnosticListener.IsEnabled();
}
#region Send
internal Activity SendStart(IList<Message> messageList)
{
Activity activity = Start("Send", () => new
{
Messages = messageList,
Entity = this.entityPath,
Endpoint = this.endpoint
},
a => SetTags(a, messageList)
);
Inject(messageList);
return activity;
}
internal void SendStop(Activity activity, IList<Message> messageList, TaskStatus? status)
{
if (activity != null)
{
DiagnosticListener.StopActivity(activity, new
{
Messages = messageList,
Entity = this.entityPath,
Endpoint = this.endpoint,
Status = status ?? TaskStatus.Faulted
});
}
}
#endregion
#region Process
internal Activity ProcessStart(Message message)
{
return ProcessStart("Process", message, () => new
{
Message = message,
Entity = this.entityPath,
Endpoint = this.endpoint
},
a => SetTags(a, message));
}
internal void ProcessStop(Activity activity, Message message, TaskStatus? status)
{
if (activity != null)
{
DiagnosticListener.StopActivity(activity, new
{
Message = message,
Entity = this.entityPath,
Endpoint = this.endpoint,
Status = status ?? TaskStatus.Faulted
});
}
}
#endregion
#region ProcessSession
internal Activity ProcessSessionStart(IMessageSession session, Message message)
{
return ProcessStart("ProcessSession", message, () => new
{
Session = session,
Message = message,
Entity = this.entityPath,
Endpoint = this.endpoint
},
a => SetTags(a, message));
}
internal void ProcessSessionStop(Activity activity, IMessageSession session, Message message, TaskStatus? status)
{
if (activity != null)
{
DiagnosticListener.StopActivity(activity, new
{
Session = session,
Message = message,
Entity = this.entityPath,
Endpoint = this.endpoint,
Status = status ?? TaskStatus.Faulted
});
}
}
#endregion
#region Schedule
internal Activity ScheduleStart(Message message, DateTimeOffset scheduleEnqueueTimeUtc)
{
Activity activity = Start("Schedule", () => new
{
Message = message,
ScheduleEnqueueTimeUtc = scheduleEnqueueTimeUtc,
Entity = this.entityPath,
Endpoint = this.endpoint
},
a => SetTags(a, message));
Inject(message);
return activity;
}
internal void ScheduleStop(Activity activity, Message message, DateTimeOffset scheduleEnqueueTimeUtc, TaskStatus? status, long sequenceNumber)
{
if (activity != null)
{
DiagnosticListener.StopActivity(activity, new
{
Message = message,
ScheduleEnqueueTimeUtc = scheduleEnqueueTimeUtc,
Entity = this.entityPath,
Endpoint = this.endpoint,
SequenceNumber = sequenceNumber,
Status = status ?? TaskStatus.Faulted
});
}
}
#endregion
#region Cancel
internal Activity CancelStart(long sequenceNumber)
{
return Start("Cancel", () => new
{
SequenceNumber = sequenceNumber,
Entity = this.entityPath,
Endpoint = this.endpoint
},
null);
}
internal void CancelStop(Activity activity, long sequenceNumber, TaskStatus? status)
{
if (activity != null)
{
DiagnosticListener.StopActivity(activity, new
{
SequenceNumber = sequenceNumber,
Entity = this.entityPath,
Endpoint = this.endpoint,
Status = status ?? TaskStatus.Faulted
});
}
}
#endregion
#region Receive
internal Activity ReceiveStart(int messageCount)
{
return Start("Receive", () => new
{
RequestedMessageCount = messageCount,
Entity = this.entityPath,
Endpoint = this.endpoint
},
null);
}
internal void ReceiveStop(Activity activity, int messageCount, TaskStatus? status, IList<Message> messageList)
{
if (activity != null)
{
SetRelatedOperations(activity, messageList);
SetTags(activity, messageList);
DiagnosticListener.StopActivity(activity, new
{
RequestedMessageCount = messageCount,
Entity = this.entityPath,
Endpoint = this.endpoint,
Status = status ?? TaskStatus.Faulted,
Messages = messageList
});
}
}
#endregion
#region Peek
internal Activity PeekStart(long fromSequenceNumber, int messageCount)
{
return Start("Peek", () => new
{
FromSequenceNumber = fromSequenceNumber,
RequestedMessageCount = messageCount,
Entity = this.entityPath,
Endpoint = this.endpoint
},
null);
}
internal void PeekStop(Activity activity, long fromSequenceNumber, int messageCount, TaskStatus? status, IList<Message> messageList)
{
if (activity != null)
{
SetRelatedOperations(activity, messageList);
SetTags(activity, messageList);
DiagnosticListener.StopActivity(activity, new
{
FromSequenceNumber = fromSequenceNumber,
RequestedMessageCount = messageCount,
Entity = this.entityPath,
Endpoint = this.endpoint,
Status = status ?? TaskStatus.Faulted,
Messages = messageList
});
}
}
#endregion
#region ReceiveDeferred
internal Activity ReceiveDeferredStart(IEnumerable<long> sequenceNumbers)
{
return Start("ReceiveDeferred", () => new
{
SequenceNumbers = sequenceNumbers,
Entity = this.entityPath,
Endpoint = this.endpoint
},
null);
}
internal void ReceiveDeferredStop(Activity activity, IEnumerable<long> sequenceNumbers, TaskStatus? status, IList<Message> messageList)
{
if (activity != null)
{
SetRelatedOperations(activity, messageList);
SetTags(activity, messageList);
DiagnosticListener.StopActivity(activity, new
{
SequenceNumbers = sequenceNumbers,
Entity = this.entityPath,
Endpoint = this.endpoint,
Messages = messageList,
Status = status ?? TaskStatus.Faulted
});
}
}
#endregion
#region Complete
internal Activity CompleteStart(IList<string> lockTokens)
{
return Start("Complete", () => new
{
LockTokens = lockTokens,
Entity = this.entityPath,
Endpoint = this.endpoint
},
null);
}
internal void CompleteStop(Activity activity, IList<string> lockTokens, TaskStatus? status)
{
if (activity != null)
{
DiagnosticListener.StopActivity(activity, new
{
LockTokens = lockTokens,
Entity = this.entityPath,
Endpoint = this.endpoint,
Status = status ?? TaskStatus.Faulted
});
}
}
#endregion
#region Dispose
internal Activity DisposeStart(string operationName, string lockToken)
{
return Start(operationName, () => new
{
LockToken = lockToken,
Entity = this.entityPath,
Endpoint = this.endpoint
},
null);
}
internal void DisposeStop(Activity activity, string lockToken, TaskStatus? status)
{
if (activity != null)
{
DiagnosticListener.StopActivity(activity, new
{
LockToken = lockToken,
Entity = this.entityPath,
Endpoint = this.endpoint,
Status = status ?? TaskStatus.Faulted
});
}
}
#endregion
#region RenewLock
internal Activity RenewLockStart(string lockToken)
{
return Start("RenewLock", () => new
{
LockToken = lockToken,
Entity = this.entityPath,
Endpoint = this.endpoint
},
null);
}
internal void RenewLockStop(Activity activity, string lockToken, TaskStatus? status, DateTime lockedUntilUtc)
{
if (activity != null)
{
DiagnosticListener.StopActivity(activity, new
{
LockToken = lockToken,
Entity = this.entityPath,
Endpoint = this.endpoint,
Status = status ?? TaskStatus.Faulted,
LockedUntilUtc = lockedUntilUtc
});
}
}
#endregion
#region AddRule
internal Activity AddRuleStart(RuleDescription description)
{
return Start("AddRule", () => new
{
Rule = description,
Entity = this.entityPath,
Endpoint = this.endpoint
},
null);
}
internal void AddRuleStop(Activity activity, RuleDescription description, TaskStatus? status)
{
if (activity != null)
{
DiagnosticListener.StopActivity(activity, new
{
Rule = description,
Entity = this.entityPath,
Endpoint = this.endpoint,
Status = status ?? TaskStatus.Faulted
});
}
}
#endregion
#region RemoveRule
internal Activity RemoveRuleStart(string ruleName)
{
return Start("RemoveRule", () => new
{
RuleName = ruleName,
Entity = this.entityPath,
Endpoint = this.endpoint
},
null);
}
internal void RemoveRuleStop(Activity activity, string ruleName, TaskStatus? status)
{
if (activity != null)
{
DiagnosticListener.StopActivity(activity, new
{
RuleName = ruleName,
Entity = this.entityPath,
Endpoint = this.endpoint,
Status = status ?? TaskStatus.Faulted
});
}
}
#endregion
#region GetRules
internal Activity GetRulesStart()
{
return Start("GetRules", () => new
{
Entity = this.entityPath,
Endpoint = this.endpoint
},
null);
}
internal void GetRulesStop(Activity activity, IEnumerable<RuleDescription> rules, TaskStatus? status)
{
if (activity != null)
{
DiagnosticListener.StopActivity(activity, new
{
Rules = rules,
Entity = this.entityPath,
Endpoint = this.endpoint,
Status = status ?? TaskStatus.Faulted
});
}
}
#endregion
#region AcceptMessageSession
internal Activity AcceptMessageSessionStart(string sessionId)
{
return Start("AcceptMessageSession", () => new
{
SessionId = sessionId,
Entity = this.entityPath,
Endpoint = this.endpoint
},
a => SetSessionTag(a, sessionId)
);
}
internal void AcceptMessageSessionStop(Activity activity, string sessionId, TaskStatus? status)
{
if (activity != null)
{
DiagnosticListener.StopActivity(activity, new
{
SessionId = sessionId,
Entity = this.entityPath,
Endpoint = this.endpoint,
Status = status ?? TaskStatus.Faulted
});
}
}
#endregion
#region GetSessionStateAsync
internal Activity GetSessionStateStart(string sessionId)
{
return Start("GetSessionState", () => new
{
SessionId = sessionId,
Entity = this.entityPath,
Endpoint = this.endpoint
},
a => SetSessionTag(a, sessionId));
}
internal void GetSessionStateStop(Activity activity, string sessionId, byte[] state, TaskStatus? status)
{
if (activity != null)
{
DiagnosticListener.StopActivity(activity, new
{
SessionId = sessionId,
Entity = this.entityPath,
Endpoint = this.endpoint,
Status = status ?? TaskStatus.Faulted,
State = state
});
}
}
#endregion
#region SetSessionState
internal Activity SetSessionStateStart(string sessionId, byte[] state)
{
return Start("SetSessionState", () => new
{
State = state,
SessionId = sessionId,
Entity = this.entityPath,
Endpoint = this.endpoint
},
a => SetSessionTag(a, sessionId));
}
internal void SetSessionStateStop(Activity activity, byte[] state, string sessionId, TaskStatus? status)
{
if (activity != null)
{
DiagnosticListener.StopActivity(activity, new
{
State = state,
SessionId = sessionId,
Entity = this.entityPath,
Endpoint = this.endpoint,
Status = status ?? TaskStatus.Faulted
});
}
}
#endregion
#region RenewSessionLock
internal Activity RenewSessionLockStart(string sessionId)
{
return Start("RenewSessionLock", () => new
{
SessionId = sessionId,
Entity = this.entityPath,
Endpoint = this.endpoint
},
a => SetSessionTag(a, sessionId));
}
internal void RenewSessionLockStop(Activity activity, string sessionId, TaskStatus? status)
{
if (activity != null)
{
DiagnosticListener.StopActivity(activity, new
{
SessionId = sessionId,
Entity = this.entityPath,
Endpoint = this.endpoint,
Status = status ?? TaskStatus.Faulted
});
}
}
#endregion
internal void ReportException(Exception ex)
{
if (DiagnosticListener.IsEnabled(ExceptionEventName))
{
DiagnosticListener.Write(ExceptionEventName,
new
{
Exception = ex,
Entity = this.entityPath,
Endpoint = this.endpoint
});
}
}
private Activity Start(string operationName, Func<object> getPayload, Action<Activity> setTags)
{
Activity activity = null;
string activityName = BaseActivityName + operationName;
if (DiagnosticListener.IsEnabled(activityName, this.entityPath))
{
activity = new Activity(activityName);
setTags?.Invoke(activity);
if (DiagnosticListener.IsEnabled(activityName + ".Start"))
{
DiagnosticListener.StartActivity(activity, getPayload());
}
else
{
activity.Start();
}
}
return activity;
}
private void Inject(IList<Message> messageList)
{
var currentActivity = Activity.Current;
if (currentActivity != null)
{
var correlationContext = SerializeCorrelationContext(currentActivity.Baggage.ToList());
foreach (var message in messageList)
{
Inject(message, currentActivity.Id, correlationContext);
}
}
}
private void Inject(Message message)
{
var currentActivity = Activity.Current;
if (currentActivity != null)
{
Inject(message, currentActivity.Id, SerializeCorrelationContext(currentActivity.Baggage.ToList()));
}
}
private void Inject(Message message, string id, string correlationContext)
{
if (!message.UserProperties.ContainsKey(ActivityIdPropertyName))
{
message.UserProperties[ActivityIdPropertyName] = id;
if (correlationContext != null)
{
message.UserProperties[CorrelationContextPropertyName] = correlationContext;
}
}
}
private string SerializeCorrelationContext(IList<KeyValuePair<string,string>> baggage)
{
if (baggage.Any())
{
return string.Join(",", baggage.Select(kvp => kvp.Key + "=" + kvp.Value));
}
return null;
}
private void SetRelatedOperations(Activity activity, IList<Message> messageList)
{
if (messageList != null && messageList.Count > 0)
{
var relatedTo = new List<string>();
foreach (var message in messageList)
{
if (message.TryExtractId(out string id))
{
relatedTo.Add(id);
}
}
if (relatedTo.Count > 0)
{
activity.AddTag(RelatedToTag, string.Join(",", relatedTo.Distinct()));
}
}
}
private Activity ProcessStart(string operationName, Message message, Func<object> getPayload, Action<Activity> setTags)
{
Activity activity = null;
string activityName = BaseActivityName + operationName;
if (DiagnosticListener.IsEnabled(activityName, entityPath))
{
var tmpActivity = message.ExtractActivity(activityName);
setTags?.Invoke(tmpActivity);
if (DiagnosticListener.IsEnabled(activityName, entityPath, tmpActivity))
{
activity = tmpActivity;
if (DiagnosticListener.IsEnabled(activityName + ".Start"))
{
DiagnosticListener.StartActivity(activity, getPayload());
}
else
{
activity.Start();
}
}
}
return activity;
}
private void SetTags(Activity activity, IList<Message> messageList)
{
var messageIds = messageList.Where(m => m.MessageId != null).Select(m => m.MessageId).ToArray();
if (messageIds.Any())
{
activity.AddTag(MessageIdTag, string.Join(",", messageIds));
}
var sessionIds = messageList.Where(m => m.SessionId != null).Select(m => m.SessionId).Distinct().ToArray();
if (sessionIds.Any())
{
activity.AddTag(SessionIdTag, string.Join(",", sessionIds));
}
}
private void SetTags(Activity activity, Message message)
{
if (message.MessageId != null)
{
activity.AddTag(MessageIdTag, message.MessageId);
}
SetSessionTag(activity, message.SessionId);
}
private void SetSessionTag(Activity activity, string sessionId)
{
if (sessionId != null)
{
activity.AddTag(SessionIdTag, sessionId);
}
}
}
}

Просмотреть файл

@ -10,11 +10,11 @@ namespace Microsoft.Azure.ServiceBus
/// </summary> /// </summary>
public class ServiceBusTimeoutException : ServiceBusException public class ServiceBusTimeoutException : ServiceBusException
{ {
internal ServiceBusTimeoutException(string message) : this(message, null) public ServiceBusTimeoutException(string message) : this(message, null)
{ {
} }
internal ServiceBusTimeoutException(string message, Exception innerException) : base(true, message, innerException) public ServiceBusTimeoutException(string message, Exception innerException) : base(true, message, innerException)
{ {
} }
} }

Просмотреть файл

@ -5,6 +5,7 @@ namespace Microsoft.Azure.ServiceBus
{ {
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Amqp; using Amqp;
@ -45,6 +46,7 @@ namespace Microsoft.Azure.ServiceBus
{ {
const int DefaultPrefetchCount = 0; const int DefaultPrefetchCount = 0;
readonly bool ownsConnection; readonly bool ownsConnection;
readonly ServiceBusDiagnosticSource diagnosticSource;
/// <summary> /// <summary>
/// Creates a new SessionClient from a <see cref="ServiceBusConnectionStringBuilder"/> /// Creates a new SessionClient from a <see cref="ServiceBusConnectionStringBuilder"/>
@ -123,6 +125,7 @@ namespace Microsoft.Azure.ServiceBus
this.ReceiveMode = receiveMode; this.ReceiveMode = receiveMode;
this.PrefetchCount = prefetchCount; this.PrefetchCount = prefetchCount;
this.CbsTokenProvider = cbsTokenProvider; this.CbsTokenProvider = cbsTokenProvider;
this.diagnosticSource = new ServiceBusDiagnosticSource(entityPath, serviceBusConnection.Endpoint);
// Register plugins on the message session. // Register plugins on the message session.
if (registeredPlugins != null) if (registeredPlugins != null)
@ -213,6 +216,10 @@ namespace Microsoft.Azure.ServiceBus
this.PrefetchCount, this.PrefetchCount,
sessionId); sessionId);
bool isDiagnosticSourceEnabled = ServiceBusDiagnosticSource.IsEnabled();
Activity activity = isDiagnosticSourceEnabled ? this.diagnosticSource.AcceptMessageSessionStart(sessionId) : null;
Task acceptMessageSessionTask = null;
var session = new MessageSession( var session = new MessageSession(
this.EntityPath, this.EntityPath,
this.EntityType, this.EntityType,
@ -226,11 +233,18 @@ namespace Microsoft.Azure.ServiceBus
try try
{ {
await this.RetryPolicy.RunOperation(() => session.GetSessionReceiverLinkAsync(serverWaitTime), serverWaitTime) acceptMessageSessionTask = this.RetryPolicy.RunOperation(
.ConfigureAwait(false); () => session.GetSessionReceiverLinkAsync(serverWaitTime),
serverWaitTime);
await acceptMessageSessionTask.ConfigureAwait(false);
} }
catch (Exception exception) catch (Exception exception)
{ {
if (isDiagnosticSourceEnabled)
{
this.diagnosticSource.ReportException(exception);
}
MessagingEventSource.Log.AmqpSessionClientAcceptMessageSessionException( MessagingEventSource.Log.AmqpSessionClientAcceptMessageSessionException(
this.ClientId, this.ClientId,
this.EntityPath, this.EntityPath,
@ -239,6 +253,10 @@ namespace Microsoft.Azure.ServiceBus
await session.CloseAsync().ConfigureAwait(false); await session.CloseAsync().ConfigureAwait(false);
throw AmqpExceptionHelper.GetClientException(exception); throw AmqpExceptionHelper.GetClientException(exception);
} }
finally
{
this.diagnosticSource.AcceptMessageSessionStop(activity, session.SessionId, acceptMessageSessionTask?.Status);
}
MessagingEventSource.Log.AmqpSessionClientAcceptMessageSessionStop( MessagingEventSource.Log.AmqpSessionClientAcceptMessageSessionStop(
this.ClientId, this.ClientId,

Просмотреть файл

@ -10,12 +10,12 @@ namespace Microsoft.Azure.ServiceBus
/// </summary> /// </summary>
public sealed class SessionLockLostException : ServiceBusException public sealed class SessionLockLostException : ServiceBusException
{ {
internal SessionLockLostException(string message) public SessionLockLostException(string message)
: this(message, null) : this(message, null)
{ {
} }
internal SessionLockLostException(string message, Exception innerException) public SessionLockLostException(string message, Exception innerException)
: base(false, message, innerException) : base(false, message, innerException)
{ {
} }

Просмотреть файл

@ -12,9 +12,9 @@ namespace Microsoft.Azure.ServiceBus
readonly object syncLock; readonly object syncLock;
SessionReceivePump sessionReceivePump; SessionReceivePump sessionReceivePump;
CancellationTokenSource sessionPumpCancellationTokenSource; CancellationTokenSource sessionPumpCancellationTokenSource;
readonly string endpoint; readonly Uri endpoint;
public SessionPumpHost(string clientId, ReceiveMode receiveMode, ISessionClient sessionClient, string endpoint) public SessionPumpHost(string clientId, ReceiveMode receiveMode, ISessionClient sessionClient, Uri endpoint)
{ {
this.syncLock = new object(); this.syncLock = new object();
this.ClientId = clientId; this.ClientId = clientId;

Просмотреть файл

@ -4,6 +4,7 @@
namespace Microsoft.Azure.ServiceBus namespace Microsoft.Azure.ServiceBus
{ {
using System; using System;
using System.Diagnostics;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Primitives; using Primitives;
@ -19,13 +20,14 @@ namespace Microsoft.Azure.ServiceBus
readonly CancellationToken pumpCancellationToken; readonly CancellationToken pumpCancellationToken;
readonly SemaphoreSlim maxConcurrentSessionsSemaphoreSlim; readonly SemaphoreSlim maxConcurrentSessionsSemaphoreSlim;
readonly SemaphoreSlim maxPendingAcceptSessionsSemaphoreSlim; readonly SemaphoreSlim maxPendingAcceptSessionsSemaphoreSlim;
private readonly ServiceBusDiagnosticSource diagnosticSource;
public SessionReceivePump(string clientId, public SessionReceivePump(string clientId,
ISessionClient client, ISessionClient client,
ReceiveMode receiveMode, ReceiveMode receiveMode,
SessionHandlerOptions sessionHandlerOptions, SessionHandlerOptions sessionHandlerOptions,
Func<IMessageSession, Message, CancellationToken, Task> callback, Func<IMessageSession, Message, CancellationToken, Task> callback,
string endpoint, Uri endpoint,
CancellationToken token) CancellationToken token)
{ {
this.client = client ?? throw new ArgumentException(nameof(client)); this.client = client ?? throw new ArgumentException(nameof(client));
@ -33,11 +35,12 @@ namespace Microsoft.Azure.ServiceBus
this.ReceiveMode = receiveMode; this.ReceiveMode = receiveMode;
this.sessionHandlerOptions = sessionHandlerOptions; this.sessionHandlerOptions = sessionHandlerOptions;
this.userOnSessionCallback = callback; this.userOnSessionCallback = callback;
this.endpoint = endpoint; this.endpoint = endpoint.Authority;
this.entityPath = client.EntityPath; this.entityPath = client.EntityPath;
this.pumpCancellationToken = token; this.pumpCancellationToken = token;
this.maxConcurrentSessionsSemaphoreSlim = new SemaphoreSlim(this.sessionHandlerOptions.MaxConcurrentSessions); this.maxConcurrentSessionsSemaphoreSlim = new SemaphoreSlim(this.sessionHandlerOptions.MaxConcurrentSessions);
this.maxPendingAcceptSessionsSemaphoreSlim = new SemaphoreSlim(this.sessionHandlerOptions.MaxConcurrentAcceptSessionCalls); this.maxPendingAcceptSessionsSemaphoreSlim = new SemaphoreSlim(this.sessionHandlerOptions.MaxConcurrentAcceptSessionCalls);
this.diagnosticSource = new ServiceBusDiagnosticSource(client.EntityPath, endpoint);
} }
ReceiveMode ReceiveMode { get; } ReceiveMode ReceiveMode { get; }
@ -146,7 +149,10 @@ namespace Microsoft.Azure.ServiceBus
} }
else else
{ {
await this.RaiseExceptionReceived(exception, ExceptionReceivedEventArgsAction.AcceptMessageSession).ConfigureAwait(false); if (!(exception is ObjectDisposedException && this.pumpCancellationToken.IsCancellationRequested))
{
await this.RaiseExceptionReceived(exception, ExceptionReceivedEventArgsAction.AcceptMessageSession).ConfigureAwait(false);
}
if (!MessagingUtilities.ShouldRetry(exception)) if (!MessagingUtilities.ShouldRetry(exception))
{ {
break; break;
@ -197,7 +203,10 @@ namespace Microsoft.Azure.ServiceBus
continue; continue;
} }
await this.RaiseExceptionReceived(exception, ExceptionReceivedEventArgsAction.Receive).ConfigureAwait(false); if (!(exception is ObjectDisposedException && this.pumpCancellationToken.IsCancellationRequested))
{
await this.RaiseExceptionReceived(exception, ExceptionReceivedEventArgsAction.Receive).ConfigureAwait(false);
}
break; break;
} }
@ -207,36 +216,54 @@ namespace Microsoft.Azure.ServiceBus
break; break;
} }
// Set the timer bool isDiagnosticSourceEnabled = ServiceBusDiagnosticSource.IsEnabled();
userCallbackTimer.Change(this.sessionHandlerOptions.MaxAutoRenewDuration, TimeSpan.FromMilliseconds(-1)); Activity activity = isDiagnosticSourceEnabled ? this.diagnosticSource.ProcessSessionStart(session, message) : null;
var callbackExceptionOccurred = false; Task processTask = null;
try try
{ {
await this.userOnSessionCallback(session, message, this.pumpCancellationToken).ConfigureAwait(false); // Set the timer
} userCallbackTimer.Change(this.sessionHandlerOptions.MaxAutoRenewDuration,
catch (Exception exception) TimeSpan.FromMilliseconds(-1));
{ var callbackExceptionOccurred = false;
MessagingEventSource.Log.MessageReceivePumpTaskException(this.clientId, session.SessionId, exception); try
await this.RaiseExceptionReceived(exception, ExceptionReceivedEventArgsAction.UserCallback).ConfigureAwait(false);
callbackExceptionOccurred = true;
if (!(exception is MessageLockLostException || exception is SessionLockLostException))
{ {
await this.AbandonMessageIfNeededAsync(session, message).ConfigureAwait(false); processTask = this.userOnSessionCallback(session, message, this.pumpCancellationToken);
await processTask.ConfigureAwait(false);
}
catch (Exception exception)
{
if (isDiagnosticSourceEnabled)
{
this.diagnosticSource.ReportException(exception);
}
MessagingEventSource.Log.MessageReceivePumpTaskException(this.clientId, session.SessionId, exception);
await this.RaiseExceptionReceived(exception, ExceptionReceivedEventArgsAction.UserCallback).ConfigureAwait(false);
callbackExceptionOccurred = true;
if (!(exception is MessageLockLostException || exception is SessionLockLostException))
{
await this.AbandonMessageIfNeededAsync(session, message).ConfigureAwait(false);
}
}
finally
{
userCallbackTimer.Change(Timeout.Infinite, Timeout.Infinite);
}
if (!callbackExceptionOccurred)
{
await this.CompleteMessageIfNeededAsync(session, message).ConfigureAwait(false);
}
else if (session.IsClosedOrClosing)
{
// If User closed the session as part of the callback, break out of the loop
break;
} }
} }
finally finally
{ {
userCallbackTimer.Change(Timeout.Infinite, Timeout.Infinite); this.diagnosticSource.ProcessSessionStop(activity, session, message, processTask?.Status);
}
if (!callbackExceptionOccurred)
{
await this.CompleteMessageIfNeededAsync(session, message).ConfigureAwait(false);
}
else if (session.IsClosedOrClosing)
{
// If User closed the session as part of the callback, break out of the loop
break;
} }
} }
} }
@ -293,9 +320,10 @@ namespace Microsoft.Azure.ServiceBus
{ {
MessagingEventSource.Log.SessionReceivePumpSessionRenewLockException(this.clientId, session.SessionId, exception); MessagingEventSource.Log.SessionReceivePumpSessionRenewLockException(this.clientId, session.SessionId, exception);
// TaskCancelled is expected here as renewTasks will be cancelled after the Complete call is made. // TaskCanceled is expected here as renewTasks will be cancelled after the Complete call is made.
// Lets not bother user with this exception. // ObjectDisposedException should only happen here because the CancellationToken was disposed at which point
if (!(exception is TaskCanceledException)) // this renew exception is not relevant anymore. Lets not bother user with this exception.
if (!(exception is TaskCanceledException) && !(exception is ObjectDisposedException))
{ {
await this.RaiseExceptionReceived(exception, ExceptionReceivedEventArgsAction.RenewLock).ConfigureAwait(false); await this.RaiseExceptionReceived(exception, ExceptionReceivedEventArgsAction.RenewLock).ConfigureAwait(false);
} }

Просмотреть файл

@ -5,6 +5,7 @@ namespace Microsoft.Azure.ServiceBus
{ {
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Azure.Amqp; using Microsoft.Azure.Amqp;
@ -52,6 +53,8 @@ namespace Microsoft.Azure.ServiceBus
int prefetchCount; int prefetchCount;
readonly object syncLock; readonly object syncLock;
readonly bool ownsConnection; readonly bool ownsConnection;
readonly ServiceBusDiagnosticSource diagnosticSource;
IInnerSubscriptionClient innerSubscriptionClient; IInnerSubscriptionClient innerSubscriptionClient;
SessionClient sessionClient; SessionClient sessionClient;
SessionPumpHost sessionPumpHost; SessionPumpHost sessionPumpHost;
@ -91,6 +94,39 @@ namespace Microsoft.Azure.ServiceBus
throw Fx.Exception.ArgumentNullOrWhiteSpace(subscriptionName); throw Fx.Exception.ArgumentNullOrWhiteSpace(subscriptionName);
} }
this.InternalTokenProvider = this.ServiceBusConnection.CreateTokenProvider();
this.CbsTokenProvider = new TokenProviderAdapter(this.InternalTokenProvider, this.ServiceBusConnection.OperationTimeout);
this.ownsConnection = true;
}
/// <summary>
/// Creates a new instance of the Subscription client using the specified endpoint, entity path, and token provider.
/// </summary>
/// <param name="endpoint">Fully qualified domain name for Service Bus. Most likely, {yournamespace}.servicebus.windows.net</param>
/// <param name="topicPath">Topic path.</param>
/// <param name="subscriptionName">Subscription name.</param>
/// <param name="tokenProvider">Token provider which will generate security tokens for authorization.</param>
/// <param name="transportType">Transport type.</param>
/// <param name="receiveMode">Mode of receive of messages. Defaults to <see cref="ReceiveMode"/>.PeekLock.</param>
/// <param name="retryPolicy">Retry policy for subscription operations. Defaults to <see cref="RetryPolicy.Default"/></param>
/// <returns></returns>
public SubscriptionClient(
string endpoint,
string topicPath,
string subscriptionName,
ITokenProvider tokenProvider,
TransportType transportType = TransportType.Amqp,
ReceiveMode receiveMode = ReceiveMode.PeekLock,
RetryPolicy retryPolicy = null)
: this(new ServiceBusNamespaceConnection(endpoint, transportType, retryPolicy), topicPath, subscriptionName, receiveMode, retryPolicy)
{
if (tokenProvider == null)
{
throw Fx.Exception.ArgumentNull(nameof(tokenProvider));
}
this.InternalTokenProvider = tokenProvider;
this.CbsTokenProvider = new TokenProviderAdapter(this.InternalTokenProvider, this.ServiceBusConnection.OperationTimeout);
this.ownsConnection = true; this.ownsConnection = true;
} }
@ -106,8 +142,7 @@ namespace Microsoft.Azure.ServiceBus
this.SubscriptionName = subscriptionName; this.SubscriptionName = subscriptionName;
this.Path = EntityNameHelper.FormatSubscriptionPath(this.TopicPath, this.SubscriptionName); this.Path = EntityNameHelper.FormatSubscriptionPath(this.TopicPath, this.SubscriptionName);
this.ReceiveMode = receiveMode; this.ReceiveMode = receiveMode;
this.TokenProvider = this.ServiceBusConnection.CreateTokenProvider(); this.diagnosticSource = new ServiceBusDiagnosticSource(this.Path, serviceBusConnection.Endpoint);
this.CbsTokenProvider = new TokenProviderAdapter(this.TokenProvider, serviceBusConnection.OperationTimeout);
MessagingEventSource.Log.SubscriptionClientCreateStop(serviceBusConnection.Endpoint.Authority, topicPath, subscriptionName, this.ClientId); MessagingEventSource.Log.SubscriptionClientCreateStop(serviceBusConnection.Endpoint.Authority, topicPath, subscriptionName, this.ClientId);
} }
@ -246,7 +281,7 @@ namespace Microsoft.Azure.ServiceBus
this.ClientId, this.ClientId,
this.ReceiveMode, this.ReceiveMode,
this.SessionClient, this.SessionClient,
this.ServiceBusConnection.Endpoint.Authority); this.ServiceBusConnection.Endpoint);
} }
} }
} }
@ -259,7 +294,7 @@ namespace Microsoft.Azure.ServiceBus
ICbsTokenProvider CbsTokenProvider { get; } ICbsTokenProvider CbsTokenProvider { get; }
TokenProvider TokenProvider { get; } ITokenProvider InternalTokenProvider { get; }
/// <summary> /// <summary>
/// Completes a <see cref="Message"/> using its lock token. This will delete the message from the subscription. /// Completes a <see cref="Message"/> using its lock token. This will delete the message from the subscription.
@ -299,7 +334,7 @@ namespace Microsoft.Azure.ServiceBus
/// <remarks> /// <remarks>
/// A lock token can be found in <see cref="Message.SystemPropertiesCollection.LockToken"/>, /// A lock token can be found in <see cref="Message.SystemPropertiesCollection.LockToken"/>,
/// only when <see cref="ReceiveMode"/> is set to <see cref="ServiceBus.ReceiveMode.PeekLock"/>. /// only when <see cref="ReceiveMode"/> is set to <see cref="ServiceBus.ReceiveMode.PeekLock"/>.
/// In order to receive a message from the deadletter sub-queue, you will need a new <see cref="IMessageReceiver"/> or <see cref="IQueueClient"/>, with the corresponding path. /// In order to receive a message from the deadletter sub-queue, you will need a new <see cref="IMessageReceiver"/> or <see cref="ISubscriptionClient"/>, with the corresponding path.
/// You can use <see cref="EntityNameHelper.FormatDeadLetterPath(string)"/> to help with this. /// You can use <see cref="EntityNameHelper.FormatDeadLetterPath(string)"/> to help with this.
/// This operation can only be performed on messages that were received by this client. /// This operation can only be performed on messages that were received by this client.
/// </remarks> /// </remarks>
@ -444,15 +479,29 @@ namespace Microsoft.Azure.ServiceBus
description.ValidateDescriptionName(); description.ValidateDescriptionName();
MessagingEventSource.Log.AddRuleStart(this.ClientId, description.Name); MessagingEventSource.Log.AddRuleStart(this.ClientId, description.Name);
bool isDiagnosticSourceEnabled = ServiceBusDiagnosticSource.IsEnabled();
Activity activity = isDiagnosticSourceEnabled ? this.diagnosticSource.AddRuleStart(description) : null;
Task addRuleTask = null;
try try
{ {
await this.InnerSubscriptionClient.OnAddRuleAsync(description).ConfigureAwait(false); addRuleTask = this.InnerSubscriptionClient.OnAddRuleAsync(description);
await addRuleTask.ConfigureAwait(false);
} }
catch (Exception exception) catch (Exception exception)
{ {
if (isDiagnosticSourceEnabled)
{
this.diagnosticSource.ReportException(exception);
}
MessagingEventSource.Log.AddRuleException(this.ClientId, exception); MessagingEventSource.Log.AddRuleException(this.ClientId, exception);
throw; throw;
} }
finally
{
this.diagnosticSource.AddRuleStop(activity, description, addRuleTask?.Status);
}
MessagingEventSource.Log.AddRuleStop(this.ClientId); MessagingEventSource.Log.AddRuleStop(this.ClientId);
} }
@ -471,16 +520,30 @@ namespace Microsoft.Azure.ServiceBus
} }
MessagingEventSource.Log.RemoveRuleStart(this.ClientId, ruleName); MessagingEventSource.Log.RemoveRuleStart(this.ClientId, ruleName);
bool isDiagnosticSourceEnabled = ServiceBusDiagnosticSource.IsEnabled();
Activity activity = isDiagnosticSourceEnabled ? this.diagnosticSource.RemoveRuleStart(ruleName) : null;
Task removeRuleTask = null;
try try
{ {
await this.InnerSubscriptionClient.OnRemoveRuleAsync(ruleName).ConfigureAwait(false); removeRuleTask = this.InnerSubscriptionClient.OnRemoveRuleAsync(ruleName);
await removeRuleTask.ConfigureAwait(false);
} }
catch (Exception exception) catch (Exception exception)
{ {
if (isDiagnosticSourceEnabled)
{
this.diagnosticSource.ReportException(exception);
}
MessagingEventSource.Log.RemoveRuleException(this.ClientId, exception); MessagingEventSource.Log.RemoveRuleException(this.ClientId, exception);
throw; throw;
} }
finally
{
this.diagnosticSource.RemoveRuleStop(activity, ruleName, removeRuleTask?.Status);
}
MessagingEventSource.Log.RemoveRuleStop(this.ClientId); MessagingEventSource.Log.RemoveRuleStop(this.ClientId);
} }
@ -493,19 +556,34 @@ namespace Microsoft.Azure.ServiceBus
this.ThrowIfClosed(); this.ThrowIfClosed();
MessagingEventSource.Log.GetRulesStart(this.ClientId); MessagingEventSource.Log.GetRulesStart(this.ClientId);
bool isDiagnosticSourceEnabled = ServiceBusDiagnosticSource.IsEnabled();
Activity activity = isDiagnosticSourceEnabled ? this.diagnosticSource.GetRulesStart() : null;
Task<IEnumerable<RuleDescription>> getRulesTask = null;
var skip = 0; var skip = 0;
var top = int.MaxValue; var top = int.MaxValue;
IEnumerable<RuleDescription> rules; IEnumerable<RuleDescription> rules = null;
try try
{ {
rules = await this.InnerSubscriptionClient.OnGetRulesAsync(top, skip); getRulesTask = this.InnerSubscriptionClient.OnGetRulesAsync(top, skip);
rules = await getRulesTask.ConfigureAwait(false);
} }
catch (Exception exception) catch (Exception exception)
{ {
if (isDiagnosticSourceEnabled)
{
this.diagnosticSource.ReportException(exception);
}
MessagingEventSource.Log.GetRulesException(this.ClientId, exception); MessagingEventSource.Log.GetRulesException(this.ClientId, exception);
throw; throw;
} }
finally
{
this.diagnosticSource.GetRulesStop(activity, rules, getRulesTask?.Status);
}
MessagingEventSource.Log.GetRulesStop(this.ClientId); MessagingEventSource.Log.GetRulesStop(this.ClientId);
return rules; return rules;

Просмотреть файл

@ -65,6 +65,35 @@ namespace Microsoft.Azure.ServiceBus
throw Fx.Exception.ArgumentNullOrWhiteSpace(entityPath); throw Fx.Exception.ArgumentNullOrWhiteSpace(entityPath);
} }
this.InternalTokenProvider = this.ServiceBusConnection.CreateTokenProvider();
this.CbsTokenProvider = new TokenProviderAdapter(this.InternalTokenProvider, this.ServiceBusConnection.OperationTimeout);
this.ownsConnection = true;
}
/// <summary>
/// Creates a new instance of the Topic client using the specified endpoint, entity path, and token provider.
/// </summary>
/// <param name="endpoint">Fully qualified domain name for Service Bus. Most likely, {yournamespace}.servicebus.windows.net</param>
/// <param name="entityPath">Topic path.</param>
/// <param name="tokenProvider">Token provider which will generate security tokens for authorization.</param>
/// <param name="transportType">Transport type.</param>
/// <param name="retryPolicy">Retry policy for topic operations. Defaults to <see cref="RetryPolicy.Default"/></param>
/// <returns></returns>
public TopicClient(
string endpoint,
string entityPath,
ITokenProvider tokenProvider,
TransportType transportType = TransportType.Amqp,
RetryPolicy retryPolicy = null)
: this(new ServiceBusNamespaceConnection(endpoint, transportType, retryPolicy), entityPath, retryPolicy)
{
if (tokenProvider == null)
{
throw Fx.Exception.ArgumentNull(nameof(tokenProvider));
}
this.InternalTokenProvider = tokenProvider;
this.CbsTokenProvider = new TokenProviderAdapter(this.InternalTokenProvider, this.ServiceBusConnection.OperationTimeout);
this.ownsConnection = true; this.ownsConnection = true;
} }
@ -77,8 +106,6 @@ namespace Microsoft.Azure.ServiceBus
this.OperationTimeout = this.ServiceBusConnection.OperationTimeout; this.OperationTimeout = this.ServiceBusConnection.OperationTimeout;
this.syncLock = new object(); this.syncLock = new object();
this.TopicName = entityPath; this.TopicName = entityPath;
this.TokenProvider = this.ServiceBusConnection.CreateTokenProvider();
this.CbsTokenProvider = new TokenProviderAdapter(this.TokenProvider, serviceBusConnection.OperationTimeout);
MessagingEventSource.Log.TopicClientCreateStop(serviceBusConnection?.Endpoint.Authority, entityPath, this.ClientId); MessagingEventSource.Log.TopicClientCreateStop(serviceBusConnection?.Endpoint.Authority, entityPath, this.ClientId);
} }
@ -130,7 +157,7 @@ namespace Microsoft.Azure.ServiceBus
ICbsTokenProvider CbsTokenProvider { get; } ICbsTokenProvider CbsTokenProvider { get; }
TokenProvider TokenProvider { get; } ITokenProvider InternalTokenProvider { get; }
/// <summary> /// <summary>
/// Sends a message to Service Bus. /// Sends a message to Service Bus.

Просмотреть файл

@ -1,22 +1,25 @@
#if NET461 namespace Microsoft.Azure.ServiceBus.UnitTests.API
namespace Microsoft.Azure.ServiceBus.UnitTests.API
{ {
using System.Runtime.CompilerServices; using System;
using System.Linq;
using ApprovalTests; using ApprovalTests;
using ApprovalTests.Reporters;
using PublicApiGenerator;
using Xunit; using Xunit;
public class ApiApprovals public class ApiApprovals
{ {
[Fact] [Fact]
[MethodImpl(MethodImplOptions.NoInlining)]
[UseReporter(typeof(DiffReporter), typeof(ClipboardReporter))]
public void ApproveAzureServiceBus() public void ApproveAzureServiceBus()
{ {
var publicApi = ApiGenerator.GeneratePublicApi(typeof(Message).Assembly); var assembly = typeof(Message).Assembly;
var publicApi = Filter(PublicApiGenerator.ApiGenerator.GeneratePublicApi(assembly, whitelistedNamespacePrefixes: new[] { "Microsoft.Azure.ServiceBus." }));
Approvals.Verify(publicApi); Approvals.Verify(publicApi);
} }
string Filter(string text)
{
return string.Join(Environment.NewLine, text.Split(new[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries)
.Where(l => !l.StartsWith("[assembly: System.Runtime.Versioning.TargetFrameworkAttribute"))
);
}
} }
} }
#endif

Просмотреть файл

@ -1,8 +1,6 @@
[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute(@"Microsoft.Azure.ServiceBus.UnitTests,PublicKey=0024000004800000940000000602000000240000525341310004000001000100fdf4acac3b2244dd8a96737e5385b31414369dc3e42f371172127252856a0650793e1f5673a16d5d78e2ac852a104bc51e6f018dca44fdd26a219c27cb2b263956a80620223c8e9c2f8913c3c903e1e453e9e4e84098afdad5f4badb8c1ebe0a7b0a4b57a08454646a65886afe3e290a791ff3260099ce0edf0bdbccafadfeb6")] [assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute(@"Microsoft.Azure.ServiceBus.UnitTests,PublicKey=0024000004800000940000000602000000240000525341310004000001000100fdf4acac3b2244dd8a96737e5385b31414369dc3e42f371172127252856a0650793e1f5673a16d5d78e2ac852a104bc51e6f018dca44fdd26a219c27cb2b263956a80620223c8e9c2f8913c3c903e1e453e9e4e84098afdad5f4badb8c1ebe0a7b0a4b57a08454646a65886afe3e290a791ff3260099ce0edf0bdbccafadfeb6")]
[assembly: System.Runtime.InteropServices.ComVisibleAttribute(false)] [assembly: System.Runtime.InteropServices.ComVisibleAttribute(false)]
[assembly: System.Runtime.InteropServices.GuidAttribute("a042adf0-ef65-4f87-b634-322a409f3d61")] [assembly: System.Runtime.InteropServices.GuidAttribute("a042adf0-ef65-4f87-b634-322a409f3d61")]
[assembly: System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.6.1", FrameworkDisplayName=".NET Framework 4.6.1")]
namespace Microsoft.Azure.ServiceBus namespace Microsoft.Azure.ServiceBus
{ {
@ -168,9 +166,21 @@ namespace Microsoft.Azure.ServiceBus
public System.TimeSpan MaxAutoRenewDuration { get; set; } public System.TimeSpan MaxAutoRenewDuration { get; set; }
public int MaxConcurrentCalls { get; set; } public int MaxConcurrentCalls { get; set; }
} }
public sealed class MessageLockLostException : Microsoft.Azure.ServiceBus.ServiceBusException { } public sealed class MessageLockLostException : Microsoft.Azure.ServiceBus.ServiceBusException
public sealed class MessagingEntityDisabledException : Microsoft.Azure.ServiceBus.ServiceBusException { } {
public sealed class MessagingEntityNotFoundException : Microsoft.Azure.ServiceBus.ServiceBusException { } public MessageLockLostException(string message) { }
public MessageLockLostException(string message, System.Exception innerException) { }
}
public sealed class MessagingEntityDisabledException : Microsoft.Azure.ServiceBus.ServiceBusException
{
public MessagingEntityDisabledException(string message) { }
public MessagingEntityDisabledException(string message, System.Exception innerException) { }
}
public sealed class MessagingEntityNotFoundException : Microsoft.Azure.ServiceBus.ServiceBusException
{
public MessagingEntityNotFoundException(string message) { }
public MessagingEntityNotFoundException(string message, System.Exception innerException) { }
}
public sealed class NoRetry : Microsoft.Azure.ServiceBus.RetryPolicy public sealed class NoRetry : Microsoft.Azure.ServiceBus.RetryPolicy
{ {
public NoRetry() { } public NoRetry() { }
@ -180,6 +190,7 @@ namespace Microsoft.Azure.ServiceBus
{ {
public QueueClient(Microsoft.Azure.ServiceBus.ServiceBusConnectionStringBuilder connectionStringBuilder, Microsoft.Azure.ServiceBus.ReceiveMode receiveMode = 0, Microsoft.Azure.ServiceBus.RetryPolicy retryPolicy = null) { } public QueueClient(Microsoft.Azure.ServiceBus.ServiceBusConnectionStringBuilder connectionStringBuilder, Microsoft.Azure.ServiceBus.ReceiveMode receiveMode = 0, Microsoft.Azure.ServiceBus.RetryPolicy retryPolicy = null) { }
public QueueClient(string connectionString, string entityPath, Microsoft.Azure.ServiceBus.ReceiveMode receiveMode = 0, Microsoft.Azure.ServiceBus.RetryPolicy retryPolicy = null) { } public QueueClient(string connectionString, string entityPath, Microsoft.Azure.ServiceBus.ReceiveMode receiveMode = 0, Microsoft.Azure.ServiceBus.RetryPolicy retryPolicy = null) { }
public QueueClient(string endpoint, string entityPath, Microsoft.Azure.ServiceBus.Primitives.ITokenProvider tokenProvider, Microsoft.Azure.ServiceBus.TransportType transportType = 0, Microsoft.Azure.ServiceBus.ReceiveMode receiveMode = 0, Microsoft.Azure.ServiceBus.RetryPolicy retryPolicy = null) { }
public override System.TimeSpan OperationTimeout { get; set; } public override System.TimeSpan OperationTimeout { get; set; }
public string Path { get; } public string Path { get; }
public int PrefetchCount { get; set; } public int PrefetchCount { get; set; }
@ -202,7 +213,11 @@ namespace Microsoft.Azure.ServiceBus
public System.Threading.Tasks.Task SendAsync(System.Collections.Generic.IList<Microsoft.Azure.ServiceBus.Message> messageList) { } public System.Threading.Tasks.Task SendAsync(System.Collections.Generic.IList<Microsoft.Azure.ServiceBus.Message> messageList) { }
public override void UnregisterPlugin(string serviceBusPluginName) { } public override void UnregisterPlugin(string serviceBusPluginName) { }
} }
public sealed class QuotaExceededException : Microsoft.Azure.ServiceBus.ServiceBusException { } public sealed class QuotaExceededException : Microsoft.Azure.ServiceBus.ServiceBusException
{
public QuotaExceededException(string message) { }
public QuotaExceededException(string message, System.Exception innerException) { }
}
public enum ReceiveMode public enum ReceiveMode
{ {
PeekLock = 0, PeekLock = 0,
@ -239,11 +254,15 @@ namespace Microsoft.Azure.ServiceBus
public Microsoft.Azure.ServiceBus.Filter Filter { get; set; } public Microsoft.Azure.ServiceBus.Filter Filter { get; set; }
public string Name { get; set; } public string Name { get; set; }
} }
public sealed class ServerBusyException : Microsoft.Azure.ServiceBus.ServiceBusException { } public sealed class ServerBusyException : Microsoft.Azure.ServiceBus.ServiceBusException
{
public ServerBusyException(string message) { }
public ServerBusyException(string message, System.Exception innerException) { }
}
public class ServiceBusCommunicationException : Microsoft.Azure.ServiceBus.ServiceBusException public class ServiceBusCommunicationException : Microsoft.Azure.ServiceBus.ServiceBusException
{ {
protected internal ServiceBusCommunicationException(string message) { } public ServiceBusCommunicationException(string message) { }
protected internal ServiceBusCommunicationException(string message, System.Exception innerException) { } public ServiceBusCommunicationException(string message, System.Exception innerException) { }
} }
public class ServiceBusConnectionStringBuilder public class ServiceBusConnectionStringBuilder
{ {
@ -273,7 +292,11 @@ namespace Microsoft.Azure.ServiceBus
public override string Message { get; } public override string Message { get; }
public string ServiceBusNamespace { get; } public string ServiceBusNamespace { get; }
} }
public class ServiceBusTimeoutException : Microsoft.Azure.ServiceBus.ServiceBusException { } public class ServiceBusTimeoutException : Microsoft.Azure.ServiceBus.ServiceBusException
{
public ServiceBusTimeoutException(string message) { }
public ServiceBusTimeoutException(string message, System.Exception innerException) { }
}
public sealed class SessionClient : Microsoft.Azure.ServiceBus.ClientEntity, Microsoft.Azure.ServiceBus.IClientEntity, Microsoft.Azure.ServiceBus.ISessionClient public sealed class SessionClient : Microsoft.Azure.ServiceBus.ClientEntity, Microsoft.Azure.ServiceBus.IClientEntity, Microsoft.Azure.ServiceBus.ISessionClient
{ {
public SessionClient(Microsoft.Azure.ServiceBus.ServiceBusConnectionStringBuilder connectionStringBuilder, Microsoft.Azure.ServiceBus.ReceiveMode receiveMode = 0, Microsoft.Azure.ServiceBus.RetryPolicy retryPolicy = null, int prefetchCount = 0) { } public SessionClient(Microsoft.Azure.ServiceBus.ServiceBusConnectionStringBuilder connectionStringBuilder, Microsoft.Azure.ServiceBus.ReceiveMode receiveMode = 0, Microsoft.Azure.ServiceBus.RetryPolicy retryPolicy = null, int prefetchCount = 0) { }
@ -298,7 +321,11 @@ namespace Microsoft.Azure.ServiceBus
public int MaxConcurrentSessions { get; set; } public int MaxConcurrentSessions { get; set; }
public System.TimeSpan MessageWaitTimeout { get; set; } public System.TimeSpan MessageWaitTimeout { get; set; }
} }
public sealed class SessionLockLostException : Microsoft.Azure.ServiceBus.ServiceBusException { } public sealed class SessionLockLostException : Microsoft.Azure.ServiceBus.ServiceBusException
{
public SessionLockLostException(string message) { }
public SessionLockLostException(string message, System.Exception innerException) { }
}
public class SqlFilter : Microsoft.Azure.ServiceBus.Filter public class SqlFilter : Microsoft.Azure.ServiceBus.Filter
{ {
public SqlFilter(string sqlExpression) { } public SqlFilter(string sqlExpression) { }
@ -317,6 +344,7 @@ namespace Microsoft.Azure.ServiceBus
{ {
public SubscriptionClient(Microsoft.Azure.ServiceBus.ServiceBusConnectionStringBuilder connectionStringBuilder, string subscriptionName, Microsoft.Azure.ServiceBus.ReceiveMode receiveMode = 0, Microsoft.Azure.ServiceBus.RetryPolicy retryPolicy = null) { } public SubscriptionClient(Microsoft.Azure.ServiceBus.ServiceBusConnectionStringBuilder connectionStringBuilder, string subscriptionName, Microsoft.Azure.ServiceBus.ReceiveMode receiveMode = 0, Microsoft.Azure.ServiceBus.RetryPolicy retryPolicy = null) { }
public SubscriptionClient(string connectionString, string topicPath, string subscriptionName, Microsoft.Azure.ServiceBus.ReceiveMode receiveMode = 0, Microsoft.Azure.ServiceBus.RetryPolicy retryPolicy = null) { } public SubscriptionClient(string connectionString, string topicPath, string subscriptionName, Microsoft.Azure.ServiceBus.ReceiveMode receiveMode = 0, Microsoft.Azure.ServiceBus.RetryPolicy retryPolicy = null) { }
public SubscriptionClient(string endpoint, string topicPath, string subscriptionName, Microsoft.Azure.ServiceBus.Primitives.ITokenProvider tokenProvider, Microsoft.Azure.ServiceBus.TransportType transportType = 0, Microsoft.Azure.ServiceBus.ReceiveMode receiveMode = 0, Microsoft.Azure.ServiceBus.RetryPolicy retryPolicy = null) { }
public override System.TimeSpan OperationTimeout { get; set; } public override System.TimeSpan OperationTimeout { get; set; }
public string Path { get; } public string Path { get; }
public int PrefetchCount { get; set; } public int PrefetchCount { get; set; }
@ -345,6 +373,7 @@ namespace Microsoft.Azure.ServiceBus
{ {
public TopicClient(Microsoft.Azure.ServiceBus.ServiceBusConnectionStringBuilder connectionStringBuilder, Microsoft.Azure.ServiceBus.RetryPolicy retryPolicy = null) { } public TopicClient(Microsoft.Azure.ServiceBus.ServiceBusConnectionStringBuilder connectionStringBuilder, Microsoft.Azure.ServiceBus.RetryPolicy retryPolicy = null) { }
public TopicClient(string connectionString, string entityPath, Microsoft.Azure.ServiceBus.RetryPolicy retryPolicy = null) { } public TopicClient(string connectionString, string entityPath, Microsoft.Azure.ServiceBus.RetryPolicy retryPolicy = null) { }
public TopicClient(string endpoint, string entityPath, Microsoft.Azure.ServiceBus.Primitives.ITokenProvider tokenProvider, Microsoft.Azure.ServiceBus.TransportType transportType = 0, Microsoft.Azure.ServiceBus.RetryPolicy retryPolicy = null) { }
public override System.TimeSpan OperationTimeout { get; set; } public override System.TimeSpan OperationTimeout { get; set; }
public string Path { get; } public string Path { get; }
public override System.Collections.Generic.IList<Microsoft.Azure.ServiceBus.Core.ServiceBusPlugin> RegisteredPlugins { get; } public override System.Collections.Generic.IList<Microsoft.Azure.ServiceBus.Core.ServiceBusPlugin> RegisteredPlugins { get; }
@ -476,6 +505,14 @@ namespace Microsoft.Azure.ServiceBus.Core
public virtual System.Threading.Tasks.Task<Microsoft.Azure.ServiceBus.Message> BeforeMessageSend(Microsoft.Azure.ServiceBus.Message message) { } public virtual System.Threading.Tasks.Task<Microsoft.Azure.ServiceBus.Message> BeforeMessageSend(Microsoft.Azure.ServiceBus.Message message) { }
} }
} }
namespace Microsoft.Azure.ServiceBus.Diagnostics
{
public class static MessageExtensions
{
public static System.Diagnostics.Activity ExtractActivity(this Microsoft.Azure.ServiceBus.Message message, string activityName = null) { }
}
}
namespace Microsoft.Azure.ServiceBus.InteropExtensions namespace Microsoft.Azure.ServiceBus.InteropExtensions
{ {
@ -488,4 +525,58 @@ namespace Microsoft.Azure.ServiceBus.InteropExtensions
{ {
public static T GetBody<T>(this Microsoft.Azure.ServiceBus.Message message, System.Runtime.Serialization.XmlObjectSerializer serializer = null) { } public static T GetBody<T>(this Microsoft.Azure.ServiceBus.Message message, System.Runtime.Serialization.XmlObjectSerializer serializer = null) { }
} }
}
namespace Microsoft.Azure.ServiceBus.Primitives
{
public class AzureActiveDirectoryTokenProvider : Microsoft.Azure.ServiceBus.Primitives.TokenProvider
{
public override System.Threading.Tasks.Task<Microsoft.Azure.ServiceBus.Primitives.SecurityToken> GetTokenAsync(string appliesTo, System.TimeSpan timeout) { }
}
public interface ITokenProvider
{
System.Threading.Tasks.Task<Microsoft.Azure.ServiceBus.Primitives.SecurityToken> GetTokenAsync(string appliesTo, System.TimeSpan timeout);
}
public class JsonSecurityToken : Microsoft.Azure.ServiceBus.Primitives.SecurityToken
{
public JsonSecurityToken(string rawToken, string audience) { }
}
public class ManagedServiceIdentityTokenProvider : Microsoft.Azure.ServiceBus.Primitives.TokenProvider
{
public ManagedServiceIdentityTokenProvider() { }
public override System.Threading.Tasks.Task<Microsoft.Azure.ServiceBus.Primitives.SecurityToken> GetTokenAsync(string appliesTo, System.TimeSpan timeout) { }
}
public class SecurityToken
{
public SecurityToken(string tokenString, System.DateTime expiresAtUtc, string audience, string tokenType) { }
public string Audience { get; }
public System.DateTime ExpiresAtUtc { get; }
public virtual string TokenType { get; }
public virtual string TokenValue { get; }
}
public class SharedAccessSignatureTokenProvider : Microsoft.Azure.ServiceBus.Primitives.TokenProvider
{
protected SharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey, System.Func<string, byte[]> customKeyEncoder, System.TimeSpan tokenTimeToLive, Microsoft.Azure.ServiceBus.Primitives.TokenScope tokenScope) { }
protected virtual string BuildSignature(string targetUri) { }
public override System.Threading.Tasks.Task<Microsoft.Azure.ServiceBus.Primitives.SecurityToken> GetTokenAsync(string appliesTo, System.TimeSpan timeout) { }
}
public abstract class TokenProvider : Microsoft.Azure.ServiceBus.Primitives.ITokenProvider
{
protected TokenProvider() { }
public static Microsoft.Azure.ServiceBus.Primitives.TokenProvider CreateAadTokenProvider(Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext authContext, Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential clientCredential) { }
public static Microsoft.Azure.ServiceBus.Primitives.TokenProvider CreateAadTokenProvider(Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext authContext, string clientId, System.Uri redirectUri, Microsoft.IdentityModel.Clients.ActiveDirectory.IPlatformParameters platformParameters, Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifier userIdentifier = null) { }
public static Microsoft.Azure.ServiceBus.Primitives.TokenProvider CreateAadTokenProvider(Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext authContext, Microsoft.IdentityModel.Clients.ActiveDirectory.ClientAssertionCertificate clientAssertionCertificate) { }
public static Microsoft.Azure.ServiceBus.Primitives.TokenProvider CreateManagedServiceIdentityTokenProvider() { }
public static Microsoft.Azure.ServiceBus.Primitives.TokenProvider CreateSharedAccessSignatureTokenProvider(string sharedAccessSignature) { }
public static Microsoft.Azure.ServiceBus.Primitives.TokenProvider CreateSharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey) { }
public static Microsoft.Azure.ServiceBus.Primitives.TokenProvider CreateSharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey, System.TimeSpan tokenTimeToLive) { }
public static Microsoft.Azure.ServiceBus.Primitives.TokenProvider CreateSharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey, Microsoft.Azure.ServiceBus.Primitives.TokenScope tokenScope) { }
public static Microsoft.Azure.ServiceBus.Primitives.TokenProvider CreateSharedAccessSignatureTokenProvider(string keyName, string sharedAccessKey, System.TimeSpan tokenTimeToLive, Microsoft.Azure.ServiceBus.Primitives.TokenScope tokenScope) { }
public abstract System.Threading.Tasks.Task<Microsoft.Azure.ServiceBus.Primitives.SecurityToken> GetTokenAsync(string appliesTo, System.TimeSpan timeout);
}
public enum TokenScope
{
Namespace = 0,
Entity = 1,
}
} }

Просмотреть файл

@ -1,34 +0,0 @@
#if NET461
using System.IO;
using System.Reflection;
using ApprovalTests;
using ApprovalTests.Namers;
namespace ApiApprover
{
public static class PublicApiApprover
{
public static void ApprovePublicApi(Assembly assembly)
{
var publicApi = PublicApiGenerator.ApiGenerator.GeneratePublicApi(assembly);
var writer = new ApprovalTextWriter(publicApi, "cs");
var approvalNamer = new AssemblyPathNamer(assembly.Location);
Approvals.Verify(writer, approvalNamer, Approvals.GetReporter());
}
private class AssemblyPathNamer : UnitTestFrameworkNamer
{
private readonly string name;
public AssemblyPathNamer(string assemblyPath)
{
name = Path.GetFileNameWithoutExtension(assemblyPath);
}
public override string Name => name;
}
}
}
#endif

Просмотреть файл

@ -0,0 +1,7 @@
using ApprovalTests.Reporters;
#if NET461
[assembly: UseReporter(typeof(XUnit2Reporter), typeof(AllFailingTestsClipboardReporter))]
#else
[assembly: UseReporter(typeof(XUnit2Reporter))]
#endif

Просмотреть файл

@ -1,801 +0,0 @@
#if NET461
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.CSharp;
using Mono.Cecil;
using Mono.Cecil.Rocks;
using ICustomAttributeProvider = Mono.Cecil.ICustomAttributeProvider;
using TypeAttributes = System.Reflection.TypeAttributes;
using System.Globalization;
// ReSharper disable BitwiseOperatorOnEnumWithoutFlags
namespace PublicApiGenerator
{
public static class ApiGenerator
{
public static string GeneratePublicApi(Assembly assembly, Type[] includeTypes = null, bool shouldIncludeAssemblyAttributes = true)
{
var assemblyResolver = new DefaultAssemblyResolver();
var assemblyPath = assembly.Location;
assemblyResolver.AddSearchDirectory(Path.GetDirectoryName(assemblyPath));
var readSymbols = File.Exists(Path.ChangeExtension(assemblyPath, ".pdb"));
var asm = AssemblyDefinition.ReadAssembly(assemblyPath, new ReaderParameters(ReadingMode.Deferred)
{
ReadSymbols = readSymbols,
AssemblyResolver = assemblyResolver,
});
return CreatePublicApiForAssembly(asm, tr => includeTypes == null || includeTypes.Any(t => t.FullName == tr.FullName && t.Assembly.FullName == tr.Module.Assembly.FullName), shouldIncludeAssemblyAttributes);
}
// TODO: Assembly references?
// TODO: Better handle namespaces - using statements? - requires non-qualified type names
static string CreatePublicApiForAssembly(AssemblyDefinition assembly, Func<TypeDefinition, bool> shouldIncludeType, bool shouldIncludeAssemblyAttributes)
{
var publicApiBuilder = new StringBuilder();
var cgo = new CodeGeneratorOptions
{
BracingStyle = "C",
BlankLinesBetweenMembers = false,
VerbatimOrder = false
};
using (var provider = new CSharpCodeProvider())
{
var compileUnit = new CodeCompileUnit();
if (shouldIncludeAssemblyAttributes && assembly.HasCustomAttributes)
{
PopulateCustomAttributes(assembly, compileUnit.AssemblyCustomAttributes);
}
var publicTypes = assembly.Modules.SelectMany(m => m.GetTypes())
.Where(t => !t.IsNested && ShouldIncludeType(t) && shouldIncludeType(t))
.OrderBy(t => t.FullName);
foreach (var publicType in publicTypes)
{
var @namespace = compileUnit.Namespaces.Cast<CodeNamespace>()
.FirstOrDefault(n => n.Name == publicType.Namespace);
if (@namespace == null)
{
@namespace = new CodeNamespace(publicType.Namespace);
compileUnit.Namespaces.Add(@namespace);
}
var typeDeclaration = CreateTypeDeclaration(publicType);
@namespace.Types.Add(typeDeclaration);
}
using (var writer = new StringWriter())
{
provider.GenerateCodeFromCompileUnit(compileUnit, writer, cgo);
var typeDeclarationText = NormaliseGeneratedCode(writer);
publicApiBuilder.AppendLine(typeDeclarationText);
}
}
return NormaliseLineEndings(publicApiBuilder.ToString().Trim());
}
static string NormaliseLineEndings(string value)
{
return Regex.Replace(value, @"\r\n|\n\r|\r|\n", Environment.NewLine);
}
static bool IsDelegate(TypeDefinition publicType)
{
return publicType.BaseType != null && publicType.BaseType.FullName == "System.MulticastDelegate";
}
static bool ShouldIncludeType(TypeDefinition t)
{
return (t.IsPublic || t.IsNestedPublic || t.IsNestedFamily) && !IsCompilerGenerated(t);
}
static bool ShouldIncludeMember(IMemberDefinition m)
{
return !IsCompilerGenerated(m) && !IsDotNetTypeMember(m) && !(m is FieldDefinition);
}
static bool IsCompilerGenerated(IMemberDefinition m)
{
return m.CustomAttributes.Any(a => a.AttributeType.FullName == "System.Runtime.CompilerServices.CompilerGeneratedAttribute");
}
static bool IsDotNetTypeMember(IMemberDefinition m)
{
if (m.DeclaringType?.FullName == null)
return false;
return m.DeclaringType.FullName.StartsWith("System") || m.DeclaringType.FullName.StartsWith("Microsoft") && !m.DeclaringType.FullName.StartsWith("Microsoft.Azure.ServiceBus");
}
static void AddMemberToTypeDeclaration(CodeTypeDeclaration typeDeclaration, IMemberDefinition memberInfo)
{
if (memberInfo is MethodDefinition methodDefinition)
{
if (methodDefinition.IsConstructor)
AddCtorToTypeDeclaration(typeDeclaration, methodDefinition);
else
AddMethodToTypeDeclaration(typeDeclaration, methodDefinition);
}
else if (memberInfo is PropertyDefinition)
{
AddPropertyToTypeDeclaration(typeDeclaration, (PropertyDefinition) memberInfo);
}
else if (memberInfo is EventDefinition)
{
typeDeclaration.Members.Add(GenerateEvent((EventDefinition)memberInfo));
}
else if (memberInfo is FieldDefinition)
{
AddFieldToTypeDeclaration(typeDeclaration, (FieldDefinition) memberInfo);
}
}
static string NormaliseGeneratedCode(StringWriter writer)
{
var gennedClass = writer.ToString();
const string autoGeneratedHeader = @"^//-+\s*$.*^//-+\s*$";
const string emptyGetSet = @"\s+{\s+get\s+{\s+}\s+set\s+{\s+}\s+}";
const string emptyGet = @"\s+{\s+get\s+{\s+}\s+}";
const string emptySet = @"\s+{\s+set\s+{\s+}\s+}";
const string getSet = @"\s+{\s+get;\s+set;\s+}";
const string get = @"\s+{\s+get;\s+}";
const string set = @"\s+{\s+set;\s+}";
gennedClass = Regex.Replace(gennedClass, autoGeneratedHeader, string.Empty,
RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline | RegexOptions.Singleline);
gennedClass = Regex.Replace(gennedClass, emptyGetSet, " { get; set; }", RegexOptions.IgnorePatternWhitespace);
gennedClass = Regex.Replace(gennedClass, getSet, " { get; set; }", RegexOptions.IgnorePatternWhitespace);
gennedClass = Regex.Replace(gennedClass, emptyGet, " { get; }", RegexOptions.IgnorePatternWhitespace);
gennedClass = Regex.Replace(gennedClass, emptySet, " { set; }", RegexOptions.IgnorePatternWhitespace);
gennedClass = Regex.Replace(gennedClass, get, " { get; }", RegexOptions.IgnorePatternWhitespace);
gennedClass = Regex.Replace(gennedClass, set, " { set; }", RegexOptions.IgnorePatternWhitespace);
gennedClass = Regex.Replace(gennedClass, @"\s+{\s+}", " { }", RegexOptions.IgnorePatternWhitespace);
gennedClass = Regex.Replace(gennedClass, @"\)\s+;", ");", RegexOptions.IgnorePatternWhitespace);
return gennedClass;
}
static CodeTypeDeclaration CreateTypeDeclaration(TypeDefinition publicType)
{
if (IsDelegate(publicType))
return CreateDelegateDeclaration(publicType);
var @static = false;
TypeAttributes attributes = 0;
if (publicType.IsPublic || publicType.IsNestedPublic)
attributes |= TypeAttributes.Public;
if (publicType.IsNestedFamily)
attributes |= TypeAttributes.NestedFamily;
if (publicType.IsSealed && !publicType.IsAbstract)
attributes |= TypeAttributes.Sealed;
else if (!publicType.IsSealed && publicType.IsAbstract && !publicType.IsInterface)
attributes |= TypeAttributes.Abstract;
else if (publicType.IsSealed && publicType.IsAbstract)
@static = true;
// Static support is a hack. CodeDOM does support it, and this isn't
// correct C#, but it's good enough for our API outline
var name = publicType.Name;
var index = name.IndexOf('`');
if (index != -1)
name = name.Substring(0, index);
var declaration = new CodeTypeDeclaration(@static ? "static " + name : name)
{
CustomAttributes = CreateCustomAttributes(publicType),
// TypeAttributes must be specified before the IsXXX as they manipulate TypeAttributes!
TypeAttributes = attributes,
IsClass = publicType.IsClass,
IsEnum = publicType.IsEnum,
IsInterface = publicType.IsInterface,
IsStruct = publicType.IsValueType && !publicType.IsPrimitive && !publicType.IsEnum,
};
if (declaration.IsInterface && publicType.BaseType != null)
throw new NotImplementedException("Base types for interfaces needs testing");
PopulateGenericParameters(publicType, declaration.TypeParameters);
if (publicType.BaseType != null && ShouldOutputBaseType(publicType))
{
if (publicType.BaseType.FullName == "System.Enum")
{
var underlyingType = publicType.GetEnumUnderlyingType();
if (underlyingType.FullName != "System.Int32")
declaration.BaseTypes.Add(CreateCodeTypeReference(underlyingType));
}
else
declaration.BaseTypes.Add(CreateCodeTypeReference(publicType.BaseType));
}
foreach(var @interface in publicType.Interfaces.OrderBy(i => i.FullName)
.Select(t => new { Reference = t, Definition = t.Resolve() })
.Where(t => ShouldIncludeType(t.Definition))
.Select(t => t.Reference))
declaration.BaseTypes.Add(CreateCodeTypeReference(@interface));
foreach (var memberInfo in publicType.GetMembers().Where(ShouldIncludeMember).OrderBy(m => m.Name))
AddMemberToTypeDeclaration(declaration, memberInfo);
// Fields should be in defined order for an enum
var fields = !publicType.IsEnum
? publicType.Fields.OrderBy(f => f.Name)
: (IEnumerable<FieldDefinition>)publicType.Fields;
foreach (var field in fields)
AddMemberToTypeDeclaration(declaration, field);
foreach (var nestedType in publicType.NestedTypes.Where(ShouldIncludeType).OrderBy(t => t.FullName))
{
var nestedTypeDeclaration = CreateTypeDeclaration(nestedType);
declaration.Members.Add(nestedTypeDeclaration);
}
return declaration;
}
static CodeTypeDeclaration CreateDelegateDeclaration(TypeDefinition publicType)
{
var invokeMethod = publicType.Methods.Single(m => m.Name == "Invoke");
var name = publicType.Name;
var index = name.IndexOf('`');
if (index != -1)
name = name.Substring(0, index);
var declaration = new CodeTypeDelegate(name)
{
Attributes = MemberAttributes.Public,
CustomAttributes = CreateCustomAttributes(publicType),
ReturnType = CreateCodeTypeReference(invokeMethod.ReturnType),
};
// CodeDOM. No support. Return type attributes.
PopulateCustomAttributes(invokeMethod.MethodReturnType, declaration.CustomAttributes, type => ModifyCodeTypeReference(type, "return:"));
PopulateGenericParameters(publicType, declaration.TypeParameters);
PopulateMethodParameters(invokeMethod, declaration.Parameters);
// Of course, CodeDOM doesn't support generic type parameters for delegates. Of course.
if (declaration.TypeParameters.Count > 0)
{
var parameterNames = from parameterType in declaration.TypeParameters.Cast<CodeTypeParameter>()
select parameterType.Name;
declaration.Name = string.Format(CultureInfo.InvariantCulture, "{0}<{1}>", declaration.Name, string.Join(", ", parameterNames));
}
return declaration;
}
static bool ShouldOutputBaseType(TypeDefinition publicType)
{
return publicType.BaseType.FullName != "System.Object" && publicType.BaseType.FullName != "System.ValueType";
}
static void PopulateGenericParameters(IGenericParameterProvider publicType, CodeTypeParameterCollection parameters)
{
foreach (var parameter in publicType.GenericParameters)
{
if (parameter.HasCustomAttributes)
throw new NotImplementedException("Attributes on type parameters is not supported. And weird");
// A little hacky. Means we get "in" and "out" prefixed on any constraints, but it's either that
// or add it as a custom attribute, which looks even weirder
var name = parameter.Name;
if (parameter.IsCovariant)
name = "out " + name;
if (parameter.IsContravariant)
name = "in " + name;
var typeParameter = new CodeTypeParameter(name)
{
HasConstructorConstraint =
parameter.HasDefaultConstructorConstraint && !parameter.HasNotNullableValueTypeConstraint
};
if (parameter.HasNotNullableValueTypeConstraint)
typeParameter.Constraints.Add(" struct"); // Extra space is a hack!
if (parameter.HasReferenceTypeConstraint)
typeParameter.Constraints.Add(" class");
foreach (var constraint in parameter.Constraints.Where(t => t.FullName != "System.ValueType"))
{
typeParameter.Constraints.Add(CreateCodeTypeReference(constraint.GetElementType()));
}
parameters.Add(typeParameter);
}
}
static CodeAttributeDeclarationCollection CreateCustomAttributes(ICustomAttributeProvider type)
{
var attributes = new CodeAttributeDeclarationCollection();
PopulateCustomAttributes(type, attributes);
return attributes;
}
static void PopulateCustomAttributes(ICustomAttributeProvider type,
CodeAttributeDeclarationCollection attributes)
{
PopulateCustomAttributes(type, attributes, ctr => ctr);
}
static void PopulateCustomAttributes(ICustomAttributeProvider type,
CodeAttributeDeclarationCollection attributes, Func<CodeTypeReference, CodeTypeReference> codeTypeModifier)
{
foreach (var customAttribute in type.CustomAttributes.Where(ShouldIncludeAttribute).OrderBy(a => a.AttributeType.FullName).ThenBy(a => ConvertAttributeToCode(codeTypeModifier, a)))
{
var attribute = GenerateCodeAttributeDeclaration(codeTypeModifier, customAttribute);
attributes.Add(attribute);
}
}
static CodeAttributeDeclaration GenerateCodeAttributeDeclaration(Func<CodeTypeReference, CodeTypeReference> codeTypeModifier, CustomAttribute customAttribute)
{
var attribute = new CodeAttributeDeclaration(codeTypeModifier(CreateCodeTypeReference(customAttribute.AttributeType)));
foreach (var arg in customAttribute.ConstructorArguments)
{
attribute.Arguments.Add(new CodeAttributeArgument(CreateInitialiserExpression(arg)));
}
foreach (var field in customAttribute.Fields.OrderBy(f => f.Name))
{
attribute.Arguments.Add(new CodeAttributeArgument(field.Name, CreateInitialiserExpression(field.Argument)));
}
foreach (var property in customAttribute.Properties.OrderBy(p => p.Name))
{
attribute.Arguments.Add(new CodeAttributeArgument(property.Name, CreateInitialiserExpression(property.Argument)));
}
return attribute;
}
// Litee: This method is used for additional sorting of custom attributes when multiple values are allowed
static object ConvertAttributeToCode(Func<CodeTypeReference, CodeTypeReference> codeTypeModifier, CustomAttribute customAttribute)
{
using (var provider = new CSharpCodeProvider())
{
var cgo = new CodeGeneratorOptions
{
BracingStyle = "C",
BlankLinesBetweenMembers = false,
VerbatimOrder = false
};
var attribute = GenerateCodeAttributeDeclaration(codeTypeModifier, customAttribute);
var declaration = new CodeTypeDeclaration("DummyClass")
{
CustomAttributes = new CodeAttributeDeclarationCollection(new[] { attribute }),
};
using (var writer = new StringWriter())
{
provider.GenerateCodeFromType(declaration, writer, cgo);
return writer.ToString();
}
}
}
static readonly HashSet<string> SkipAttributeNames = new HashSet<string>
{
"System.CodeDom.Compiler.GeneratedCodeAttribute",
"System.ComponentModel.EditorBrowsableAttribute",
"System.Runtime.CompilerServices.AsyncStateMachineAttribute",
"System.Runtime.CompilerServices.CompilerGeneratedAttribute",
"System.Runtime.CompilerServices.CompilationRelaxationsAttribute",
"System.Runtime.CompilerServices.ExtensionAttribute",
"System.Runtime.CompilerServices.RuntimeCompatibilityAttribute",
"System.Reflection.DefaultMemberAttribute",
"System.Diagnostics.DebuggableAttribute",
"System.Diagnostics.DebuggerNonUserCodeAttribute",
"System.Diagnostics.DebuggerStepThroughAttribute",
"System.Reflection.AssemblyCompanyAttribute",
"System.Reflection.AssemblyConfigurationAttribute",
"System.Reflection.AssemblyCopyrightAttribute",
"System.Reflection.AssemblyDescriptionAttribute",
"System.Reflection.AssemblyFileVersionAttribute",
"System.Reflection.AssemblyInformationalVersionAttribute",
"System.Reflection.AssemblyProductAttribute",
"System.Reflection.AssemblyTitleAttribute",
"System.Reflection.AssemblyTrademarkAttribute"
};
static bool ShouldIncludeAttribute(CustomAttribute attribute)
{
var attributeTypeDefinition = attribute.AttributeType.Resolve();
return !SkipAttributeNames.Contains(attribute.AttributeType.FullName) && attributeTypeDefinition.IsPublic;
}
static CodeExpression CreateInitialiserExpression(CustomAttributeArgument attributeArgument)
{
if (attributeArgument.Value is CustomAttributeArgument)
{
return CreateInitialiserExpression((CustomAttributeArgument) attributeArgument.Value);
}
if (attributeArgument.Value is CustomAttributeArgument[])
{
var initialisers = from argument in (CustomAttributeArgument[]) attributeArgument.Value
select CreateInitialiserExpression(argument);
return new CodeArrayCreateExpression(CreateCodeTypeReference(attributeArgument.Type), initialisers.ToArray());
}
var type = attributeArgument.Type.Resolve();
var value = attributeArgument.Value;
if (type.BaseType != null && type.BaseType.FullName == "System.Enum")
{
var originalValue = Convert.ToInt64(value);
if (type.CustomAttributes.Any(a => a.AttributeType.FullName == "System.FlagsAttribute"))
{
//var allFlags = from f in type.Fields
// where f.Constant != null
// let v = Convert.ToInt64(f.Constant)
// where v == 0 || (originalValue & v) != 0
// select (CodeExpression)new CodeFieldReferenceExpression(typeExpression, f.Name);
//return allFlags.Aggregate((current, next) => new CodeBinaryOperatorExpression(current, CodeBinaryOperatorType.BitwiseOr, next));
// I'd rather use the above, as it's just using the CodeDOM, but it puts
// brackets around each CodeBinaryOperatorExpression
var flags = from f in type.Fields
where f.Constant != null
let v = Convert.ToInt64(f.Constant)
where v == 0 || (originalValue & v) != 0
select type.FullName + "." + f.Name;
return new CodeSnippetExpression(flags.Aggregate((current, next) => current + " | " + next));
}
var allFlags = from f in type.Fields
where f.Constant != null
let v = Convert.ToInt64(f.Constant)
where v == originalValue
select new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(CreateCodeTypeReference(type)), f.Name);
return allFlags.FirstOrDefault();
}
if (type.FullName == "System.Type" && value is TypeReference)
{
return new CodeTypeOfExpression(CreateCodeTypeReference((TypeReference)value));
}
if (value is string)
{
// CodeDOM outputs a verbatim string. Any string with \n is treated as such, so normalise
// it to make it easier for comparisons
value = Regex.Replace((string)value, @"\n", "\\n");
value = Regex.Replace((string)value, @"\r\n|\r\\n", "\\r\\n");
}
return new CodePrimitiveExpression(value);
}
static void AddCtorToTypeDeclaration(CodeTypeDeclaration typeDeclaration, MethodDefinition member)
{
if (member.IsAssembly || member.IsPrivate)
return;
var method = new CodeConstructor
{
CustomAttributes = CreateCustomAttributes(member),
Name = member.Name,
Attributes = GetMethodAttributes(member)
};
PopulateMethodParameters(member, method.Parameters);
typeDeclaration.Members.Add(method);
}
static void AddMethodToTypeDeclaration(CodeTypeDeclaration typeDeclaration, MethodDefinition member)
{
if (member.IsAssembly || member.IsPrivate || member.IsSpecialName)
return;
var returnType = CreateCodeTypeReference(member.ReturnType);
var method = new CodeMemberMethod
{
Name = member.Name,
Attributes = GetMethodAttributes(member),
CustomAttributes = CreateCustomAttributes(member),
ReturnType = returnType,
};
PopulateCustomAttributes(member.MethodReturnType, method.ReturnTypeCustomAttributes);
PopulateGenericParameters(member, method.TypeParameters);
PopulateMethodParameters(member, method.Parameters, IsExtensionMethod(member));
typeDeclaration.Members.Add(method);
}
static bool IsExtensionMethod(ICustomAttributeProvider method)
{
return method.CustomAttributes.Any(a => a.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
}
static void PopulateMethodParameters(IMethodSignature member,
CodeParameterDeclarationExpressionCollection parameters, bool isExtension = false)
{
foreach (var parameter in member.Parameters)
{
FieldDirection direction = 0;
if (parameter.IsOut)
direction |= FieldDirection.Out;
else if (parameter.ParameterType.IsByReference)
direction |= FieldDirection.Ref;
var parameterType = parameter.ParameterType.IsByReference
? parameter.ParameterType.GetElementType()
: parameter.ParameterType;
var type = CreateCodeTypeReference(parameterType);
if (isExtension)
{
type = ModifyCodeTypeReference(type, "this");
isExtension = false;
}
var name = parameter.HasConstant
? string.Format(CultureInfo.InvariantCulture, "{0} = {1}", parameter.Name, FormatParameterConstant(parameter))
: parameter.Name;
var expression = new CodeParameterDeclarationExpression(type, name)
{
Direction = direction,
CustomAttributes = CreateCustomAttributes(parameter)
};
parameters.Add(expression);
}
}
static object FormatParameterConstant(IConstantProvider parameter)
{
return parameter.Constant is string ? string.Format(CultureInfo.InvariantCulture, "\"{0}\"", parameter.Constant) : (parameter.Constant ?? "null");
}
static MemberAttributes GetMethodAttributes(MethodDefinition method)
{
MemberAttributes access = 0;
if (method.IsFamily)
access = MemberAttributes.Family;
if (method.IsPublic)
access = MemberAttributes.Public;
if (method.IsAssembly)
access = MemberAttributes.Assembly;
if (method.IsFamilyAndAssembly)
access = MemberAttributes.FamilyAndAssembly;
if (method.IsFamilyOrAssembly)
access = MemberAttributes.FamilyOrAssembly;
MemberAttributes scope = 0;
if (method.IsStatic)
scope |= MemberAttributes.Static;
if (method.IsFinal || !method.IsVirtual)
scope |= MemberAttributes.Final;
if (method.IsAbstract)
scope |= MemberAttributes.Abstract;
if (method.IsVirtual && !method.IsNewSlot)
scope |= MemberAttributes.Override;
MemberAttributes vtable = 0;
if (IsHidingMethod(method))
vtable = MemberAttributes.New;
return access | scope | vtable;
}
static bool IsHidingMethod(MethodDefinition method)
{
var typeDefinition = method.DeclaringType;
// If we're an interface, just try and find any method with the same signature
// in any of the interfaces that we implement
if (typeDefinition.IsInterface)
{
var interfaceMethods = from @interfaceReference in typeDefinition.Interfaces
let interfaceDefinition = @interfaceReference.Resolve()
where interfaceDefinition != null
select interfaceDefinition.Methods;
return interfaceMethods.Any(ms => MetadataResolver.GetMethod(ms, method) != null);
}
// If we're not an interface, find a base method that isn't virtual
return !method.IsVirtual && GetBaseTypes(typeDefinition).Any(d => MetadataResolver.GetMethod(d.Methods, method) != null);
}
static IEnumerable<TypeDefinition> GetBaseTypes(TypeDefinition type)
{
var baseType = type.BaseType;
while (baseType != null)
{
var definition = baseType.Resolve();
if (definition == null)
yield break;
yield return definition;
baseType = baseType.DeclaringType;
}
}
static void AddPropertyToTypeDeclaration(CodeTypeDeclaration typeDeclaration, PropertyDefinition member)
{
var getterAttributes = member.GetMethod != null ? GetMethodAttributes(member.GetMethod) : 0;
var setterAttributes = member.SetMethod != null ? GetMethodAttributes(member.SetMethod) : 0;
if (!HasVisiblePropertyMethod(getterAttributes) && !HasVisiblePropertyMethod(setterAttributes))
return;
var propertyAttributes = GetPropertyAttributes(getterAttributes, setterAttributes);
var propertyType = member.PropertyType.IsGenericParameter
? new CodeTypeReference(member.PropertyType.Name)
: CreateCodeTypeReference(member.PropertyType);
var property = new CodeMemberProperty
{
Name = member.Name,
Type = propertyType,
Attributes = propertyAttributes,
CustomAttributes = CreateCustomAttributes(member),
HasGet = member.GetMethod != null && HasVisiblePropertyMethod(getterAttributes),
HasSet = member.SetMethod != null && HasVisiblePropertyMethod(setterAttributes)
};
// Here's a nice hack, because hey, guess what, the CodeDOM doesn't support
// attributes on getters or setters
if (member.GetMethod != null && member.GetMethod.HasCustomAttributes)
{
PopulateCustomAttributes(member.GetMethod, property.CustomAttributes, type => ModifyCodeTypeReference(type, "get:"));
}
if (member.SetMethod != null && member.SetMethod.HasCustomAttributes)
{
PopulateCustomAttributes(member.SetMethod, property.CustomAttributes, type => ModifyCodeTypeReference(type, "set:"));
}
foreach (var parameter in member.Parameters)
{
property.Parameters.Add(
new CodeParameterDeclarationExpression(CreateCodeTypeReference(parameter.ParameterType),
parameter.Name));
}
// TODO: CodeDOM has no support for different access modifiers for getters and setters
// TODO: CodeDOM has no support for attributes on setters or getters - promote to property?
typeDeclaration.Members.Add(property);
}
static MemberAttributes GetPropertyAttributes(MemberAttributes getterAttributes, MemberAttributes setterAttributes)
{
MemberAttributes access = 0;
var getterAccess = getterAttributes & MemberAttributes.AccessMask;
var setterAccess = setterAttributes & MemberAttributes.AccessMask;
if (getterAccess == MemberAttributes.Public || setterAccess == MemberAttributes.Public)
access = MemberAttributes.Public;
else if (getterAccess == MemberAttributes.Family || setterAccess == MemberAttributes.Family)
access = MemberAttributes.Family;
else if (getterAccess == MemberAttributes.FamilyAndAssembly || setterAccess == MemberAttributes.FamilyAndAssembly)
access = MemberAttributes.FamilyAndAssembly;
else if (getterAccess == MemberAttributes.FamilyOrAssembly || setterAccess == MemberAttributes.FamilyOrAssembly)
access = MemberAttributes.FamilyOrAssembly;
else if (getterAccess == MemberAttributes.Assembly || setterAccess == MemberAttributes.Assembly)
access = MemberAttributes.Assembly;
else if (getterAccess == MemberAttributes.Private || setterAccess == MemberAttributes.Private)
access = MemberAttributes.Private;
// Scope should be the same for getter and setter. If one isn't specified, it'll be 0
var getterScope = getterAttributes & MemberAttributes.ScopeMask;
var setterScope = setterAttributes & MemberAttributes.ScopeMask;
var scope = (MemberAttributes) Math.Max((int) getterScope, (int) setterScope);
// Vtable should be the same for getter and setter. If one isn't specified, it'll be 0
var getterVtable = getterAttributes & MemberAttributes.VTableMask;
var setterVtable = setterAttributes & MemberAttributes.VTableMask;
var vtable = (MemberAttributes) Math.Max((int) getterVtable, (int) setterVtable);
return access | scope | vtable;
}
static bool HasVisiblePropertyMethod(MemberAttributes attributes)
{
var access = attributes & MemberAttributes.AccessMask;
return access == MemberAttributes.Public || access == MemberAttributes.Family ||
access == MemberAttributes.FamilyOrAssembly;
}
static CodeTypeMember GenerateEvent(EventDefinition eventDefinition)
{
var @event = new CodeMemberEvent
{
Name = eventDefinition.Name,
Attributes = MemberAttributes.Public | MemberAttributes.Final,
CustomAttributes = CreateCustomAttributes(eventDefinition),
Type = CreateCodeTypeReference(eventDefinition.EventType)
};
return @event;
}
static void AddFieldToTypeDeclaration(CodeTypeDeclaration typeDeclaration, FieldDefinition memberInfo)
{
if (memberInfo.IsPrivate || memberInfo.IsAssembly || memberInfo.IsSpecialName)
return;
MemberAttributes attributes = 0;
if (memberInfo.HasConstant)
attributes |= MemberAttributes.Const;
if (memberInfo.IsFamily)
attributes |= MemberAttributes.Family;
if (memberInfo.IsPublic)
attributes |= MemberAttributes.Public;
if (memberInfo.IsStatic && !memberInfo.HasConstant)
attributes |= MemberAttributes.Static;
// TODO: Values for readonly fields are set in the ctor
var codeTypeReference = CreateCodeTypeReference(memberInfo.FieldType);
if (memberInfo.IsInitOnly)
codeTypeReference = MakeReadonly(codeTypeReference);
var field = new CodeMemberField(codeTypeReference, memberInfo.Name)
{
Attributes = attributes,
CustomAttributes = CreateCustomAttributes(memberInfo)
};
if (memberInfo.HasConstant)
field.InitExpression = new CodePrimitiveExpression(memberInfo.Constant);
typeDeclaration.Members.Add(field);
}
static CodeTypeReference MakeReadonly(CodeTypeReference typeReference)
{
return ModifyCodeTypeReference(typeReference, "readonly");
}
static CodeTypeReference ModifyCodeTypeReference(CodeTypeReference typeReference, string modifier)
{
using (var provider = new CSharpCodeProvider())
return new CodeTypeReference(modifier + " " + provider.GetTypeOutput(typeReference));
}
static CodeTypeReference CreateCodeTypeReference(TypeReference type)
{
var typeName = GetTypeName(type);
return new CodeTypeReference(typeName, CreateGenericArguments(type));
}
static string GetTypeName(TypeReference type)
{
if (type.IsGenericParameter)
return type.Name;
if (!type.IsNested)
{
return (!string.IsNullOrEmpty(type.Namespace) ? (type.Namespace + ".") : "") + type.Name;
}
return GetTypeName(type.DeclaringType) + "." + type.Name;
}
static CodeTypeReference[] CreateGenericArguments(TypeReference type)
{
if (!(type is IGenericInstance genericInstance))
{
return null;
}
var genericArguments = new List<CodeTypeReference>();
foreach (var argument in genericInstance.GenericArguments)
{
genericArguments.Add(CreateCodeTypeReference(argument));
}
return genericArguments.ToArray();
}
}
static class CecilEx
{
public static IEnumerable<IMemberDefinition> GetMembers(this TypeDefinition type)
{
return type.Fields.Cast<IMemberDefinition>()
.Concat(type.Methods)
.Concat(type.Properties)
.Concat(type.Events);
}
}
}
#endif

Просмотреть файл

@ -0,0 +1,535 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.Azure.ServiceBus.UnitTests.Diagnostics
{
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Xunit;
public abstract class DiagnosticsTests : IDisposable
{
protected ConcurrentQueue<(string eventName, object payload, Activity activity)> events;
protected FakeDiagnosticListener listener;
protected IDisposable subscription;
protected const int maxWaitSec = 10;
protected abstract string EntityName { get; }
private bool disposed = false;
internal DiagnosticsTests()
{
this.events = new ConcurrentQueue<(string eventName, object payload, Activity activity)>();
this.listener = new FakeDiagnosticListener(kvp =>
{
TestUtility.Log($"Diagnostics event: {kvp.Key}, Activity Id: {Activity.Current?.Id}");
if (kvp.Key.Contains("Exception"))
{
TestUtility.Log($"Exception {kvp.Value}");
}
this.events.Enqueue((kvp.Key, kvp.Value, Activity.Current));
});
this.subscription = DiagnosticListener.AllListeners.Subscribe(this.listener);
}
#region Send
protected void AssertSendStart(string name, object payload, Activity activity, Activity parentActivity, int messageCount = 1)
{
Assert.Equal("Microsoft.Azure.ServiceBus.Send.Start", name);
AssertCommonPayloadProperties(payload);
var messages = GetPropertyValueFromAnonymousTypeInstance<IList<Message>>(payload, "Messages");
Assert.Equal(messageCount, messages.Count);
Assert.NotNull(activity);
Assert.Equal(parentActivity, activity.Parent);
AssertTags(messages, activity);
}
protected void AssertSendStop(string name, object payload, Activity activity, Activity sendActivity, int messageCount = 1)
{
Assert.Equal("Microsoft.Azure.ServiceBus.Send.Stop", name);
AssertCommonStopPayloadProperties(payload);
if (sendActivity != null)
{
Assert.Equal(sendActivity, activity);
}
var messages = GetPropertyValueFromAnonymousTypeInstance<IList<Message>>(payload, "Messages");
Assert.Equal(messageCount, messages.Count);
}
#endregion
#region Complete
protected void AssertCompleteStart(string name, object payload, Activity activity, Activity parentActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.Complete.Start", name);
AssertCommonPayloadProperties(payload);
GetPropertyValueFromAnonymousTypeInstance<IList<string>>(payload, "LockTokens");
Assert.NotNull(activity);
Assert.Equal(parentActivity, activity.Parent);
}
protected void AssertCompleteStop(string name, object payload, Activity activity, Activity completeActivity, Activity parentActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.Complete.Stop", name);
AssertCommonStopPayloadProperties(payload);
if (completeActivity != null)
{
Assert.Equal(completeActivity, activity);
}
var tokens = GetPropertyValueFromAnonymousTypeInstance<IList<string>>(payload, "LockTokens");
Assert.Equal(1, tokens.Count);
}
#endregion
#region Abandon
protected void AssertAbandonStart(string name, object payload, Activity activity, Activity parentActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.Abandon.Start", name);
AssertCommonPayloadProperties(payload);
GetPropertyValueFromAnonymousTypeInstance<string>(payload, "LockToken");
Assert.NotNull(activity);
Assert.Equal(parentActivity, activity.Parent);
}
protected void AssertAbandonStop(string name, object payload, Activity activity, Activity abandonActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.Abandon.Stop", name);
AssertCommonStopPayloadProperties(payload);
if (abandonActivity != null)
{
Assert.Equal(abandonActivity, activity);
}
GetPropertyValueFromAnonymousTypeInstance<string>(payload, "LockToken");
}
#endregion
#region Defer
protected void AssertDeferStart(string name, object payload, Activity activity, Activity parentActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.Defer.Start", name);
AssertCommonPayloadProperties(payload);
GetPropertyValueFromAnonymousTypeInstance<string>(payload, "LockToken");
Assert.NotNull(activity);
Assert.Equal(parentActivity, activity.Parent);
}
protected void AssertDeferStop(string name, object payload, Activity activity, Activity deferActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.Defer.Stop", name);
AssertCommonStopPayloadProperties(payload);
if (deferActivity != null)
{
Assert.Equal(deferActivity, activity);
}
GetPropertyValueFromAnonymousTypeInstance<string>(payload, "LockToken");
}
#endregion
#region DeadLetter
protected void AssertDeadLetterStart(string name, object payload, Activity activity, Activity parentActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.DeadLetter.Start", name);
AssertCommonPayloadProperties(payload);
GetPropertyValueFromAnonymousTypeInstance<string>(payload, "LockToken");
Assert.NotNull(activity);
Assert.Equal(parentActivity, activity.Parent);
}
protected void AssertDeadLetterStop(string name, object payload, Activity activity, Activity deadLetterActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.DeadLetter.Stop", name);
AssertCommonStopPayloadProperties(payload);
if (deadLetterActivity != null)
{
Assert.Equal(deadLetterActivity, activity);
}
GetPropertyValueFromAnonymousTypeInstance<string>(payload, "LockToken");
}
#endregion
#region Schedule
protected void AssertScheduleStart(string name, object payload, Activity activity, Activity parentActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.Schedule.Start", name);
AssertCommonPayloadProperties(payload);
var message = GetPropertyValueFromAnonymousTypeInstance<Message>(payload, "Message");
GetPropertyValueFromAnonymousTypeInstance<DateTimeOffset>(payload, "ScheduleEnqueueTimeUtc");
Assert.NotNull(activity);
Assert.Equal(parentActivity, activity.Parent);
AssertTags(message, activity);
}
protected void AssertScheduleStop(string name, object payload, Activity activity, Activity scheduleActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.Schedule.Stop", name);
AssertCommonStopPayloadProperties(payload);
Assert.Equal(scheduleActivity, activity);
var message = GetPropertyValueFromAnonymousTypeInstance<Message>(payload, "Message");
GetPropertyValueFromAnonymousTypeInstance<DateTimeOffset>(payload, "ScheduleEnqueueTimeUtc");
GetPropertyValueFromAnonymousTypeInstance<long>(payload, "SequenceNumber");
Assert.NotNull(message);
Assert.Contains("Diagnostic-Id", message.UserProperties.Keys);
if (scheduleActivity.Baggage.Any())
{
Assert.Contains("Correlation-Context", message.UserProperties.Keys);
}
}
#endregion
#region Cancel
protected void AssertCancelStart(string name, object payload, Activity activity, Activity parentActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.Cancel.Start", name);
AssertCommonPayloadProperties(payload);
GetPropertyValueFromAnonymousTypeInstance<long>(payload, "SequenceNumber");
Assert.NotNull(activity);
Assert.Equal(parentActivity, activity.Parent);
}
protected void AssertCancelStop(string name, object payload, Activity activity, Activity scheduleActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.Cancel.Stop", name);
AssertCommonStopPayloadProperties(payload);
Assert.Equal(scheduleActivity, activity);
GetPropertyValueFromAnonymousTypeInstance<long>(payload, "SequenceNumber");
}
#endregion
#region Receive
protected int AssertReceiveStart(string name, object payload, Activity activity, int messagesCount = 1)
{
Assert.Equal("Microsoft.Azure.ServiceBus.Receive.Start", name);
AssertCommonPayloadProperties(payload);
var count = GetPropertyValueFromAnonymousTypeInstance<int>(payload, "RequestedMessageCount");
if (messagesCount != -1)
{
Assert.Equal(messagesCount, count);
}
Assert.NotNull(activity);
Assert.Null(activity.Parent);
return count;
}
protected int AssertReceiveStop(string name, object payload, Activity activity, Activity receiveActivity, Activity sendActivity, int sentMessagesCount = 1, int receivedMessagesCount = 1)
{
Assert.Equal("Microsoft.Azure.ServiceBus.Receive.Stop", name);
AssertCommonStopPayloadProperties(payload);
if (receiveActivity != null)
{
Assert.Equal(receiveActivity, activity);
}
Assert.Equal(sentMessagesCount, GetPropertyValueFromAnonymousTypeInstance<int>(payload, "RequestedMessageCount"));
var messages = GetPropertyValueFromAnonymousTypeInstance<IList<Message>>(payload, "Messages");
if (receivedMessagesCount != -1)
{
Assert.Equal(receivedMessagesCount, messages.Count);
}
if (sendActivity != null)
{
Assert.Contains(sendActivity.Id, activity.Tags.Single(t => t.Key == "RelatedTo").Value);
}
AssertTags(messages, activity);
return messages.Count;
}
#endregion
#region ReceiveDeferred
protected void AssertReceiveDeferredStart(string name, object payload, Activity activity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.ReceiveDeferred.Start", name);
AssertCommonPayloadProperties(payload);
Assert.Single(GetPropertyValueFromAnonymousTypeInstance<IEnumerable<long>>(payload, "SequenceNumbers"));
Assert.NotNull(activity);
Assert.Null(activity.Parent);
}
protected void AssertReceiveDeferredStop(string name, object payload, Activity activity, Activity receiveActivity, Activity sendActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.ReceiveDeferred.Stop", name);
AssertCommonStopPayloadProperties(payload);
if (receiveActivity != null)
{
Assert.Equal(receiveActivity, activity);
}
var messages = GetPropertyValueFromAnonymousTypeInstance<IList<Message>>(payload, "Messages");
Assert.Equal(1, messages.Count);
Assert.Single(GetPropertyValueFromAnonymousTypeInstance<IEnumerable<long>>(payload, "SequenceNumbers"));
Assert.Equal(sendActivity.Id, activity.Tags.Single(t => t.Key == "RelatedTo").Value);
AssertTags(messages, activity);
}
#endregion
#region Process
protected void AssertProcessStart(string name, object payload, Activity activity, Activity sendActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.Process.Start", name);
AssertCommonPayloadProperties(payload);
var message = GetPropertyValueFromAnonymousTypeInstance<Message>(payload, "Message");
Assert.NotNull(activity);
Assert.Null(activity.Parent);
Assert.Equal(sendActivity.Id, activity.ParentId);
Assert.Equal(sendActivity.Baggage.OrderBy(p => p.Key), activity.Baggage.OrderBy(p => p.Key));
AssertTags(message, activity);
}
protected void AssertProcessStop(string name, object payload, Activity activity, Activity processActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.Process.Stop", name);
AssertCommonStopPayloadProperties(payload);
if (processActivity != null)
{
Assert.Equal(processActivity, activity);
}
}
#endregion
#region Peek
protected void AssertPeekStart(string name, object payload, Activity activity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.Peek.Start", name);
AssertCommonPayloadProperties(payload);
GetPropertyValueFromAnonymousTypeInstance<long>(payload, "FromSequenceNumber");
Assert.Equal(1, GetPropertyValueFromAnonymousTypeInstance<int>(payload, "RequestedMessageCount"));
Assert.NotNull(activity);
Assert.Null(activity.Parent);
}
protected void AssertPeekStop(string name, object payload, Activity activity, Activity peekActivity, Activity sendActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.Peek.Stop", name);
AssertCommonStopPayloadProperties(payload);
if (peekActivity != null)
{
Assert.Equal(peekActivity, activity);
}
GetPropertyValueFromAnonymousTypeInstance<long>(payload, "FromSequenceNumber");
Assert.Equal(1, GetPropertyValueFromAnonymousTypeInstance<int>(payload, "RequestedMessageCount"));
var messages = GetPropertyValueFromAnonymousTypeInstance<IList<Message>>(payload, "Messages");
AssertTags(messages, activity);
Assert.Equal(sendActivity.Id, activity.Tags.Single(t => t.Key == "RelatedTo").Value);
}
#endregion
#region RenewLock
protected void AssertRenewLockStart(string name, object payload, Activity activity, Activity parentActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.RenewLock.Start", name);
AssertCommonPayloadProperties(payload);
GetPropertyValueFromAnonymousTypeInstance<string>(payload, "LockToken");
Assert.NotNull(activity);
Assert.Equal(parentActivity, activity.Parent);
}
protected void AssertRenewLockStop(string name, object payload, Activity activity, Activity renewLockActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.RenewLock.Stop", name);
AssertCommonStopPayloadProperties(payload);
if (renewLockActivity != null)
{
Assert.Equal(renewLockActivity, activity);
}
GetPropertyValueFromAnonymousTypeInstance<string>(payload, "LockToken");
GetPropertyValueFromAnonymousTypeInstance<DateTime>(payload, "LockedUntilUtc");
}
#endregion
protected void AssertTags(IList<Message> messageList, Activity activity)
{
var messagesWithId = messageList.Where(m => m.MessageId != null).ToArray();
if (messagesWithId.Any())
{
Assert.Contains("MessageId", activity.Tags.Select(t => t.Key));
string messageIdTag = activity.Tags.Single(t => t.Key == "MessageId").Value;
foreach (var m in messagesWithId)
{
Assert.Contains(m.MessageId, messageIdTag);
}
}
var messagesWithSessionId = messageList.Where(m => m.SessionId != null).ToArray();
if (messagesWithSessionId.Any())
{
Assert.Contains("SessionId", activity.Tags.Select(t => t.Key));
string sessionIdTag = activity.Tags.Single(t => t.Key == "SessionId").Value;
foreach (var m in messagesWithSessionId)
{
Assert.Contains(m.SessionId, sessionIdTag);
}
}
}
protected void AssertTags(Message message, Activity activity)
{
if (message.MessageId != null)
{
Assert.Contains("MessageId", activity.Tags.Select(t => t.Key));
Assert.Equal(message.MessageId, activity.Tags.Single(t => t.Key == "MessageId").Value);
}
if (message.SessionId != null)
{
Assert.Contains("SessionId", activity.Tags.Select(t => t.Key));
Assert.Equal(message.SessionId, activity.Tags.Single(t => t.Key == "SessionId").Value);
}
}
protected void AssertException(string name, object payload, Activity activity, Activity parentActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.Exception", name);
AssertCommonPayloadProperties(payload);
GetPropertyValueFromAnonymousTypeInstance<Exception>(payload, "Exception");
Assert.NotNull(activity);
if (parentActivity != null)
{
Assert.Equal(parentActivity, activity.Parent);
}
}
protected void AssertCommonPayloadProperties(object eventPayload)
{
var entity = GetPropertyValueFromAnonymousTypeInstance<string>(eventPayload, "Entity");
GetPropertyValueFromAnonymousTypeInstance<Uri>(eventPayload, "Endpoint");
Assert.Equal(this.EntityName, entity);
}
protected void AssertCommonStopPayloadProperties(object eventPayload)
{
AssertCommonPayloadProperties(eventPayload);
var status = GetPropertyValueFromAnonymousTypeInstance<TaskStatus>(eventPayload, "Status");
Assert.Equal(TaskStatus.RanToCompletion, status);
}
protected T GetPropertyValueFromAnonymousTypeInstance<T>(object obj, string propertyName)
{
Type t = obj.GetType();
PropertyInfo p = t.GetRuntimeProperty(propertyName);
object propertyValue = p.GetValue(obj);
Assert.NotNull(propertyValue);
Assert.IsAssignableFrom<T>(propertyValue);
return (T)propertyValue;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (this.disposed)
return;
if (disposing)
{
//Thread.Sleep(90000);
this.listener?.Disable();
while (this.events.TryDequeue(out var evnt))
{
}
while (Activity.Current != null)
{
Activity.Current.Stop();
}
this.listener?.Dispose();
this.subscription?.Dispose();
this.events = null;
this.listener = null;
this.subscription = null;
}
this.disposed = true;
}
}
}

Просмотреть файл

@ -0,0 +1,131 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.Azure.ServiceBus.UnitTests.Diagnostics
{
using System.Linq;
using Microsoft.Azure.ServiceBus.Diagnostics;
using Xunit;
public class ExtractActivityTests
{
[Fact]
[DisplayTestMethodName]
void ValidIdAndContextAreExtracted()
{
var message = new Message();
message.UserProperties["Diagnostic-Id"] = "diagnostic-id";
message.UserProperties["Correlation-Context"] = "k1=v1";
var activity = message.ExtractActivity();
Assert.Equal("diagnostic-id", activity.ParentId);
Assert.Equal("diagnostic-id", activity.RootId);
Assert.Null(activity.Id);
var baggage = activity.Baggage.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
Assert.Equal(1, baggage.Count);
Assert.Contains("k1", baggage.Keys);
Assert.Equal("v1", baggage["k1"]);
}
[Fact]
[DisplayTestMethodName]
void ValidIdAndMultipleContextAreExtracted()
{
var message = new Message();
message.UserProperties["Diagnostic-Id"] = "diagnostic-id";
message.UserProperties["Correlation-Context"] = "k1=v1,k2=v2,k3=v3";
var activity = message.ExtractActivity();
Assert.Equal("diagnostic-id", activity.ParentId);
Assert.Equal("diagnostic-id", activity.RootId);
Assert.Null(activity.Id);
var baggage = activity.Baggage.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
Assert.Equal(3, baggage.Count);
Assert.Contains("k1", baggage.Keys);
Assert.Contains("k2", baggage.Keys);
Assert.Contains("k3", baggage.Keys);
Assert.Equal("v1", baggage["k1"]);
Assert.Equal("v2", baggage["k2"]);
Assert.Equal("v3", baggage["k3"]);
}
[Fact]
[DisplayTestMethodName]
void ActivityNameCouldBeChanged()
{
var message = new Message();
message.UserProperties["Diagnostic-Id"] = "diagnostic-id";
var activity = message.ExtractActivity("My activity");
Assert.Equal("My activity", activity.OperationName);
}
[Fact]
[DisplayTestMethodName]
void ValidIdAndNoContextAreExtracted()
{
var message = new Message();
message.UserProperties["Diagnostic-Id"] = "diagnostic-id";
var activity = message.ExtractActivity();
Assert.Equal("diagnostic-id", activity.ParentId);
Assert.Equal("diagnostic-id", activity.RootId);
Assert.Null(activity.Id);
var baggage = activity.Baggage.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
Assert.Equal(0, baggage.Count);
}
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData("not valid context")]
[InlineData("not,valid,context")]
[DisplayTestMethodName]
void ValidIdAndInvalidContextAreExtracted(string context)
{
var message = new Message();
message.UserProperties["Diagnostic-Id"] = "diagnostic-id";
message.UserProperties["Correlation-Context"] = context;
var activity = message.ExtractActivity();
Assert.Equal("diagnostic-id", activity.ParentId);
Assert.Equal("diagnostic-id", activity.RootId);
Assert.Null(activity.Id);
var baggage = activity.Baggage.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
Assert.Equal(0, baggage.Count);
}
[Theory]
[InlineData(null)]
[InlineData("")]
[DisplayTestMethodName]
void EmptyIdResultsInActivityWithoutParent(string id)
{
var message = new Message();
message.UserProperties["Diagnostic-Id"] = id;
message.UserProperties["Correlation-Context"] = "k1=v1,k2=v2";
var activity = message.ExtractActivity();
Assert.Null(activity.ParentId);
Assert.Null(activity.RootId);
Assert.Null(activity.Id);
var baggage = activity.Baggage.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
// baggage is ignored in absence of Id
Assert.Equal(0, baggage.Count);
}
}
}

Просмотреть файл

@ -0,0 +1,92 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.Azure.ServiceBus.UnitTests.Diagnostics
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
public sealed class FakeDiagnosticListener : IObserver<DiagnosticListener>, IDisposable
{
private IDisposable subscription;
private class FakeDiagnosticSourceWriteObserver : IObserver<KeyValuePair<string, object>>
{
private readonly Action<KeyValuePair<string, object>> writeCallback;
public FakeDiagnosticSourceWriteObserver(Action<KeyValuePair<string, object>> writeCallback)
{
this.writeCallback = writeCallback;
}
public void OnCompleted()
{
}
public void OnError(Exception error)
{
}
public void OnNext(KeyValuePair<string, object> value)
{
this.writeCallback(value);
}
}
private readonly Action<KeyValuePair<string, object>> writeCallback;
private Func<string, object, object, bool> writeObserverEnabled = (name, arg1, arg2) => false;
public FakeDiagnosticListener(Action<KeyValuePair<string, object>> writeCallback)
{
this.writeCallback = writeCallback;
}
public void OnCompleted()
{
}
public void OnError(Exception error)
{
}
public void OnNext(DiagnosticListener value)
{
if (value.Name.Equals("Microsoft.Azure.ServiceBus"))
{
this.subscription = value.Subscribe(new FakeDiagnosticSourceWriteObserver(this.writeCallback), this.IsEnabled);
}
}
public void Enable()
{
this.writeObserverEnabled = (name, arg1, arg2) => true;
}
public void Enable(Func<string, bool> writeObserverEnabled)
{
this.writeObserverEnabled = (name, arg1, arg2) => writeObserverEnabled(name);
}
public void Enable(Func<string, object, object, bool> writeObserverEnabled)
{
this.writeObserverEnabled = writeObserverEnabled;
}
public void Disable()
{
this.writeObserverEnabled = (name, arg1, arg2) => false;
}
private bool IsEnabled(string s, object arg1, object arg2)
{
return this.writeObserverEnabled.Invoke(s, arg1, arg2);
}
public void Dispose()
{
this.Disable();
this.subscription?.Dispose();
}
}
}

Просмотреть файл

@ -0,0 +1,498 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.Azure.ServiceBus.UnitTests.Diagnostics
{
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
public sealed class QueueClientDiagnosticsTests : DiagnosticsTests
{
protected override string EntityName => TestConstants.NonPartitionedQueueName;
private QueueClient queueClient;
private bool disposed = false;
[Fact]
[DisplayTestMethodName]
async Task EventsAreNotFiredWhenDiagnosticsIsDisabled()
{
this.queueClient = new QueueClient(TestUtility.NamespaceConnectionString, TestConstants.NonPartitionedQueueName, ReceiveMode.ReceiveAndDelete);
Activity processActivity = null;
ManualResetEvent processingDone = new ManualResetEvent(false);
this.listener.Disable();
await TestUtility.SendMessagesAsync(this.queueClient.InnerSender, 1);
this.queueClient.RegisterMessageHandler((msg, ct) => {
processActivity = Activity.Current;
processingDone.Set();
return Task.CompletedTask;
},
exArgs => Task.CompletedTask);
processingDone.WaitOne(TimeSpan.FromSeconds(maxWaitSec));
Assert.True(this.events.IsEmpty);
Assert.Null(processActivity);
}
[Fact]
[DisplayTestMethodName]
async Task EventsAreNotFiredWhenDiagnosticsIsDisabledForQueue()
{
this.queueClient = new QueueClient(TestUtility.NamespaceConnectionString, TestConstants.NonPartitionedQueueName,
ReceiveMode.ReceiveAndDelete);
Activity processActivity = null;
ManualResetEvent processingDone = new ManualResetEvent(false);
this.listener.Enable((name, queueName, arg) =>
queueName == null || queueName.ToString() != TestConstants.NonPartitionedQueueName);
await TestUtility.SendMessagesAsync(this.queueClient.InnerSender, 1);
this.queueClient.RegisterMessageHandler((msg, ct) =>
{
processActivity = Activity.Current;
processingDone.Set();
return Task.CompletedTask;
},
exArgs => Task.CompletedTask);
processingDone.WaitOne(TimeSpan.FromSeconds(maxWaitSec));
Assert.True(this.events.IsEmpty);
Assert.Null(processActivity);
}
[Fact]
[DisplayTestMethodName]
async Task SendAndHandlerFireEvents()
{
this.queueClient = new QueueClient(TestUtility.NamespaceConnectionString, TestConstants.NonPartitionedQueueName,
ReceiveMode.PeekLock);
Activity parentActivity = new Activity("test").AddBaggage("k1", "v1").AddBaggage("k2", "v2");
Activity processActivity = null;
bool exceptionCalled = false;
ManualResetEvent processingDone = new ManualResetEvent(false);
this.listener.Enable((name, queueName, arg) => !name.Contains("Receive") && !name.Contains("Exception"));
parentActivity.Start();
await TestUtility.SendSessionMessagesAsync(this.queueClient.InnerSender, 1, 1);
parentActivity.Stop();
this.queueClient.RegisterMessageHandler((msg, ct) =>
{
processActivity = Activity.Current;
processingDone.Set();
return Task.CompletedTask;
},
exArgs =>
{
exceptionCalled = true;
return Task.CompletedTask;
});
processingDone.WaitOne(TimeSpan.FromSeconds(maxWaitSec));
Assert.True(this.events.TryDequeue(out var sendStart));
AssertSendStart(sendStart.eventName, sendStart.payload, sendStart.activity, parentActivity);
Assert.True(this.events.TryDequeue(out var sendStop));
AssertSendStop(sendStop.eventName, sendStop.payload, sendStop.activity, sendStart.activity);
Assert.True(this.events.TryDequeue(out var processStart));
AssertProcessStart(processStart.eventName, processStart.payload, processStart.activity,
sendStart.activity);
// message is processed, but complete happens after that
// let's wat until Complete starts and ends and Process ends
int wait = 0;
while (wait++ < maxWaitSec && this.events.Count < 3)
{
await Task.Delay(TimeSpan.FromSeconds(1));
}
Assert.True(this.events.TryDequeue(out var completeStart));
AssertCompleteStart(completeStart.eventName, completeStart.payload, completeStart.activity,
processStart.activity);
Assert.True(this.events.TryDequeue(out var completeStop));
AssertCompleteStop(completeStop.eventName, completeStop.payload, completeStop.activity,
completeStart.activity, processStart.activity);
Assert.True(this.events.TryDequeue(out var processStop));
AssertProcessStop(processStop.eventName, processStop.payload, processStop.activity,
processStart.activity);
Assert.False(this.events.TryDequeue(out var evnt));
Assert.Equal(processStop.activity, processActivity);
Assert.False(exceptionCalled);
}
[Fact]
[DisplayTestMethodName]
async Task SendAndHandlerFireExceptionEvents()
{
this.queueClient = new QueueClient(TestUtility.NamespaceConnectionString, TestConstants.NonPartitionedQueueName,
ReceiveMode.PeekLock);
bool exceptionCalled = false;
ManualResetEvent processingDone = new ManualResetEvent(false);
await TestUtility.SendMessagesAsync(this.queueClient.InnerSender, 1);
this.listener.Enable((name, queueName, arg) => !name.EndsWith(".Start") && !name.Contains("Receive") );
int count = 0;
this.queueClient.RegisterMessageHandler((msg, ct) =>
{
if (count++ == 0)
{
throw new Exception("123");
}
processingDone.Set();
return Task.CompletedTask;
},
exArgs =>
{
exceptionCalled = true;
return Task.CompletedTask;
});
processingDone.WaitOne(TimeSpan.FromSeconds(maxWaitSec));
Assert.True(exceptionCalled);
// message is processed, but abandon happens after that
// let's spin until Complete call starts and ends
int wait = 0;
while (wait++ < maxWaitSec && this.events.Count < 3)
{
await Task.Delay(TimeSpan.FromSeconds(1));
}
Assert.True(this.events.TryDequeue(out var abandonStop));
AssertAbandonStop(abandonStop.eventName, abandonStop.payload, abandonStop.activity, null);
Assert.True(this.events.TryDequeue(out var exception));
AssertException(exception.eventName, exception.payload, exception.activity, null);
Assert.True(this.events.TryDequeue(out var processStop));
AssertProcessStop(processStop.eventName, processStop.payload, processStop.activity, null);
Assert.Equal(processStop.activity, abandonStop.activity.Parent);
Assert.Equal(processStop.activity, exception.activity);
// message will be processed and compelted again
wait = 0;
while (wait++ < maxWaitSec && this.events.Count < 2 )
{
await Task.Delay(TimeSpan.FromSeconds(1));
}
Assert.True(this.events.TryDequeue(out var completeStop));
AssertCompleteStop(completeStop.eventName, completeStop.payload, completeStop.activity, null, null);
Assert.True(this.events.TryDequeue(out processStop));
AssertProcessStop(processStop.eventName, processStop.payload, processStop.activity, null);
Assert.True(this.events.IsEmpty);
}
[Fact]
[DisplayTestMethodName]
async Task AbandonCompleteFireEvents()
{
this.queueClient = new QueueClient(TestUtility.NamespaceConnectionString, TestConstants.NonPartitionedQueueName,
ReceiveMode.PeekLock);
await TestUtility.SendMessagesAsync(this.queueClient.InnerSender, 1);
var messages = await TestUtility.ReceiveMessagesAsync(this.queueClient.InnerReceiver, 1);
this.listener.Enable((name, queueName, arg) => name.Contains("Abandon") || name.Contains("Complete"));
await TestUtility.AbandonMessagesAsync(this.queueClient.InnerReceiver, messages);
messages = await TestUtility.ReceiveMessagesAsync(this.queueClient.InnerReceiver, 1);
await TestUtility.CompleteMessagesAsync(this.queueClient.InnerReceiver, messages);
Assert.True(this.events.TryDequeue(out var abandonStart));
AssertAbandonStart(abandonStart.eventName, abandonStart.payload, abandonStart.activity, null);
Assert.True(this.events.TryDequeue(out var abandonStop));
AssertAbandonStop(abandonStop.eventName, abandonStop.payload, abandonStop.activity,
abandonStart.activity);
Assert.True(this.events.TryDequeue(out var completeStart));
AssertCompleteStart(completeStart.eventName, completeStart.payload, completeStart.activity, null);
Assert.True(this.events.TryDequeue(out var completeStop));
AssertCompleteStop(completeStop.eventName, completeStop.payload, completeStop.activity,
completeStart.activity, null);
Assert.True(this.events.IsEmpty);
}
[Fact]
[DisplayTestMethodName]
async Task BatchSendReceiveFireEvents()
{
this.queueClient = new QueueClient(TestUtility.NamespaceConnectionString, TestConstants.NonPartitionedQueueName,
ReceiveMode.ReceiveAndDelete);
this.listener.Enable( (name, queuName, arg) => name.Contains("Send") || name.Contains("Receive") );
await TestUtility.SendMessagesAsync(this.queueClient.InnerSender, 2);
await TestUtility.SendMessagesAsync(this.queueClient.InnerSender, 3);
var messages = await TestUtility.ReceiveMessagesAsync(this.queueClient.InnerReceiver, 5);
Assert.True(this.events.TryDequeue(out var sendStart1));
AssertSendStart(sendStart1.eventName, sendStart1.payload, sendStart1.activity, null, 2);
Assert.True(this.events.TryDequeue(out var sendStop1));
AssertSendStop(sendStop1.eventName, sendStop1.payload, sendStop1.activity, sendStop1.activity, 2);
Assert.True(this.events.TryDequeue(out var sendStart2));
AssertSendStart(sendStart2.eventName, sendStart2.payload, sendStart2.activity, null, 3);
Assert.True(this.events.TryDequeue(out var sendStop2));
AssertSendStop(sendStop2.eventName, sendStop2.payload, sendStop2.activity, sendStop2.activity, 3);
int receivedStopCount = 0;
string relatedTo = "";
while (this.events.TryDequeue(out var receiveStart))
{
var startCount = AssertReceiveStart(receiveStart.eventName, receiveStart.payload, receiveStart.activity,
-1);
Assert.True(this.events.TryDequeue(out var receiveStop));
receivedStopCount += AssertReceiveStop(receiveStop.eventName, receiveStop.payload, receiveStop.activity,
receiveStart.activity, null, startCount, -1);
relatedTo += receiveStop.activity.Tags.Single(t => t.Key == "RelatedTo").Value;
}
Assert.Equal(5, receivedStopCount);
Assert.Contains(sendStart1.activity.Id, relatedTo);
Assert.Contains(sendStart2.activity.Id, relatedTo);
Assert.True(this.events.IsEmpty);
}
[Fact]
[DisplayTestMethodName]
async Task PeekFireEvents()
{
this.queueClient = new QueueClient(TestUtility.NamespaceConnectionString, TestConstants.NonPartitionedQueueName, ReceiveMode.PeekLock);
this.listener.Enable((name, queuName, arg) => name.Contains("Send") || name.Contains("Peek"));
await TestUtility.SendMessagesAsync(this.queueClient.InnerSender, 1);
await TestUtility.PeekMessageAsync(this.queueClient.InnerReceiver);
this.listener.Disable();
var messages = await TestUtility.ReceiveMessagesAsync(this.queueClient.InnerReceiver, 1);
await TestUtility.CompleteMessagesAsync(this.queueClient.InnerReceiver, messages);
Assert.True(this.events.TryDequeue(out var sendStart));
AssertSendStart(sendStart.eventName, sendStart.payload, sendStart.activity, null);
Assert.True(this.events.TryDequeue(out var sendStop));
AssertSendStop(sendStop.eventName, sendStop.payload, sendStop.activity, sendStart.activity);
Assert.True(this.events.TryDequeue(out var peekStart));
AssertPeekStart(peekStart.eventName, peekStart.payload, peekStart.activity);
Assert.True(this.events.TryDequeue(out var peekStop));
AssertPeekStop(peekStop.eventName, peekStop.payload, peekStop.activity, peekStart.activity, sendStart.activity);
Assert.True(this.events.IsEmpty);
}
[Fact]
[DisplayTestMethodName]
async Task DeadLetterFireEvents()
{
this.queueClient = new QueueClient(TestUtility.NamespaceConnectionString, TestConstants.NonPartitionedQueueName,
ReceiveMode.PeekLock);
await TestUtility.SendMessagesAsync(this.queueClient.InnerSender, 1);
var messages = await TestUtility.ReceiveMessagesAsync(this.queueClient.InnerReceiver, 1);
this.listener.Enable((name, queuName, arg) => name.Contains("DeadLetter"));
await TestUtility.DeadLetterMessagesAsync(this.queueClient.InnerReceiver, messages);
this.listener.Disable();
QueueClient deadLetterQueueClient = null;
try
{
deadLetterQueueClient = new QueueClient(TestUtility.NamespaceConnectionString,
EntityNameHelper.FormatDeadLetterPath(this.queueClient.QueueName), ReceiveMode.ReceiveAndDelete);
await TestUtility.ReceiveMessagesAsync(deadLetterQueueClient.InnerReceiver, 1);
}
finally
{
deadLetterQueueClient?.CloseAsync().Wait(TimeSpan.FromSeconds(maxWaitSec));
}
Assert.True(this.events.TryDequeue(out var deadLetterStart));
AssertDeadLetterStart(deadLetterStart.eventName, deadLetterStart.payload, deadLetterStart.activity, null);
Assert.True(this.events.TryDequeue(out var deadLetterStop));
AssertDeadLetterStop(deadLetterStop.eventName, deadLetterStop.payload, deadLetterStop.activity,
deadLetterStart.activity);
Assert.True(this.events.IsEmpty);
}
[Fact]
[DisplayTestMethodName]
async Task RenewLockFireEvents()
{
this.queueClient = new QueueClient(TestUtility.NamespaceConnectionString, TestConstants.NonPartitionedQueueName,
ReceiveMode.PeekLock);
await TestUtility.SendMessagesAsync(this.queueClient.InnerSender, 1);
var messages = await TestUtility.ReceiveMessagesAsync(this.queueClient.InnerReceiver, 1);
this.listener.Enable((name, queuName, arg) => name.Contains("RenewLock"));
await this.queueClient.InnerReceiver.RenewLockAsync(messages[0]);
this.listener.Disable();
await TestUtility.CompleteMessagesAsync(this.queueClient.InnerReceiver, messages);
Assert.True(this.events.TryDequeue(out var renewStart));
AssertRenewLockStart(renewStart.eventName, renewStart.payload, renewStart.activity, null);
Assert.True(this.events.TryDequeue(out var renewStop));
AssertRenewLockStop(renewStop.eventName, renewStop.payload, renewStop.activity, renewStart.activity);
Assert.True(this.events.IsEmpty);
}
[Fact]
[DisplayTestMethodName]
async Task DeferReceiveDeferredFireEvents()
{
this.queueClient = new QueueClient(TestUtility.NamespaceConnectionString, TestConstants.NonPartitionedQueueName,
ReceiveMode.PeekLock);
this.listener.Enable((name, queuName, arg) => name.Contains("Send") || name.Contains("Defer") || name.Contains("Receive"));
await TestUtility.SendMessagesAsync(this.queueClient.InnerSender, 1);
var messages = await TestUtility.ReceiveMessagesAsync(this.queueClient.InnerReceiver, 1);
await TestUtility.DeferMessagesAsync(this.queueClient.InnerReceiver, messages);
var message = await this.queueClient.InnerReceiver.ReceiveDeferredMessageAsync(messages[0]
.SystemProperties
.SequenceNumber);
this.listener.Disable();
await TestUtility.CompleteMessagesAsync(this.queueClient.InnerReceiver, new[] {message});
Assert.True(this.events.TryDequeue(out var sendStart));
Assert.True(this.events.TryDequeue(out var sendStop));
Assert.True(this.events.TryDequeue(out var receiveStart));
Assert.True(this.events.TryDequeue(out var receiveStop));
Assert.True(this.events.TryDequeue(out var deferStart));
AssertDeferStart(deferStart.eventName, deferStart.payload, deferStart.activity, null);
Assert.True(this.events.TryDequeue(out var deferStop));
AssertDeferStop(deferStop.eventName, deferStop.payload, deferStop.activity, deferStart.activity);
Assert.True(this.events.TryDequeue(out var receiveDeferredStart));
AssertReceiveDeferredStart(receiveDeferredStart.eventName, receiveDeferredStart.payload,
receiveDeferredStart.activity);
Assert.True(this.events.TryDequeue(out var receiveDeferredStop));
AssertReceiveDeferredStop(receiveDeferredStop.eventName, receiveDeferredStop.payload,
receiveDeferredStop.activity, receiveDeferredStart.activity, sendStart.activity);
Assert.True(this.events.IsEmpty);
}
[Fact]
[DisplayTestMethodName]
async Task SendAndHandlerFilterOutStartEvents()
{
this.queueClient = new QueueClient(TestUtility.NamespaceConnectionString, TestConstants.NonPartitionedQueueName, ReceiveMode.ReceiveAndDelete);
ManualResetEvent processingDone = new ManualResetEvent(false);
this.listener.Enable((name, queueName, arg) => !name.EndsWith("Start") && !name.Contains("Receive") && !name.Contains("Exception"));
await TestUtility.SendMessagesAsync(this.queueClient.InnerSender, 1);
this.queueClient.RegisterMessageHandler((msg, ct) =>
{
processingDone.Set();
return Task.CompletedTask;
},
exArgs => Task.CompletedTask);
processingDone.WaitOne(TimeSpan.FromSeconds(maxWaitSec));
Assert.True(this.events.TryDequeue(out var sendStop));
AssertSendStop(sendStop.eventName, sendStop.payload, sendStop.activity, null);
int wait = 0;
while (wait++ < maxWaitSec && this.events.Count < 1)
{
await Task.Delay(TimeSpan.FromSeconds(1));
}
Assert.True(this.events.TryDequeue(out var processStop));
AssertProcessStop(processStop.eventName, processStop.payload, processStop.activity, null);
Assert.True(this.events.IsEmpty);
}
[Fact]
[DisplayTestMethodName]
async Task ScheduleAndCancelFireEvents()
{
this.queueClient = new QueueClient(TestUtility.NamespaceConnectionString, TestConstants.NonPartitionedQueueName,
ReceiveMode.ReceiveAndDelete);
Activity parentActivity = new Activity("test");
this.listener.Enable((name, queuName, arg) => name.Contains("Schedule") || name.Contains("Cancel"));
parentActivity.Start();
var sequenceNumber = await this.queueClient.InnerSender.ScheduleMessageAsync(new Message(), DateTimeOffset.UtcNow.AddHours(1));
await this.queueClient.InnerSender.CancelScheduledMessageAsync(sequenceNumber);
Assert.True(this.events.TryDequeue(out var scheduleStart));
AssertScheduleStart(scheduleStart.eventName, scheduleStart.payload, scheduleStart.activity,
parentActivity);
Assert.True(this.events.TryDequeue(out var scheduleStop));
AssertScheduleStop(scheduleStop.eventName, scheduleStop.payload, scheduleStop.activity,
scheduleStart.activity);
Assert.True(this.events.TryDequeue(out var cancelStart));
AssertCancelStart(cancelStart.eventName, cancelStart.payload, cancelStart.activity, parentActivity);
Assert.True(this.events.TryDequeue(out var cancelStop));
AssertCancelStop(cancelStop.eventName, cancelStop.payload, cancelStop.activity, cancelStart.activity);
Assert.True(this.events.IsEmpty);
}
protected override void Dispose(bool disposing)
{
if (this.disposed)
return;
if (disposing)
{
this.queueClient?.CloseAsync().Wait(TimeSpan.FromSeconds(maxWaitSec));
}
this.disposed = true;
base.Dispose(disposing);
}
}
}

Просмотреть файл

@ -0,0 +1,337 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.Azure.ServiceBus.UnitTests.Diagnostics
{
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus.Core;
using Xunit;
public class SessionDiagnosticsTests : DiagnosticsTests
{
protected override string EntityName => TestConstants.SessionNonPartitionedQueueName;
private IMessageSession messageSession;
private SessionClient sessionClient;
private MessageSender messageSender;
private QueueClient queueClient;
private bool disposed;
[Fact]
[DisplayTestMethodName]
async Task AcceptSetAndGetStateGetFireEvents()
{
this.messageSender = new MessageSender(TestUtility.NamespaceConnectionString,
TestConstants.SessionNonPartitionedQueueName);
this.sessionClient = new SessionClient(TestUtility.NamespaceConnectionString,
TestConstants.SessionNonPartitionedQueueName, ReceiveMode.ReceiveAndDelete);
this.listener.Enable();
var sessionId = Guid.NewGuid().ToString();
await this.messageSender.SendAsync(new Message
{
MessageId = "messageId",
SessionId = sessionId
});
this.messageSession = await this.sessionClient.AcceptMessageSessionAsync(sessionId);
await this.messageSession.SetStateAsync(new byte[] {1});
await this.messageSession.GetStateAsync();
await this.messageSession.SetStateAsync(new byte[] { });
await this.messageSession.ReceiveAsync();
Assert.True(events.TryDequeue(out var sendStart));
AssertSendStart(sendStart.eventName, sendStart.payload, sendStart.activity, null);
Assert.True(events.TryDequeue(out var sendStop));
AssertSendStop(sendStop.eventName, sendStop.payload, sendStop.activity, sendStart.activity);
Assert.True(events.TryDequeue(out var acceptStart));
AssertAcceptMessageSessionStart(acceptStart.eventName, acceptStart.payload, acceptStart.activity);
Assert.True(events.TryDequeue(out var acceptStop));
AssertAcceptMessageSessionStop(acceptStop.eventName, acceptStop.payload, acceptStop.activity,
acceptStart.activity);
Assert.True(events.TryDequeue(out var setStateStart));
AssertSetSessionStateStart(setStateStart.eventName, setStateStart.payload, setStateStart.activity);
Assert.True(events.TryDequeue(out var setStateStop));
AssertSetSessionStateStop(setStateStop.eventName, setStateStop.payload, setStateStop.activity,
setStateStart.activity);
Assert.True(events.TryDequeue(out var getStateStart));
AssertGetSessionStateStart(getStateStart.eventName, getStateStart.payload, getStateStart.activity);
Assert.True(events.TryDequeue(out var getStateStop));
AssertGetSessionStateStop(getStateStop.eventName, getStateStop.payload, getStateStop.activity,
getStateStop.activity);
Assert.True(events.TryDequeue(out var setStateStart2));
Assert.True(events.TryDequeue(out var setStateStop2));
Assert.True(events.TryDequeue(out var receiveStart));
AssertReceiveStart(receiveStart.eventName, receiveStart.payload, receiveStart.activity);
Assert.True(events.TryDequeue(out var receiveStop));
AssertReceiveStop(receiveStop.eventName, receiveStop.payload, receiveStop.activity, receiveStart.activity,
sendStart.activity);
Assert.True(events.IsEmpty);
}
[Fact]
[DisplayTestMethodName]
async Task EventsAreNotFiredWhenDiagnosticsIsDisabled()
{
this.messageSender = new MessageSender(TestUtility.NamespaceConnectionString,
TestConstants.SessionNonPartitionedQueueName);
this.sessionClient = new SessionClient(TestUtility.NamespaceConnectionString,
TestConstants.SessionNonPartitionedQueueName, ReceiveMode.ReceiveAndDelete);
this.listener.Disable();
var sessionId = Guid.NewGuid().ToString();
await this.messageSender.SendAsync(new Message
{
MessageId = "messageId",
SessionId = sessionId
});
this.messageSession = await sessionClient.AcceptMessageSessionAsync(sessionId);
await this.messageSession.SetStateAsync(new byte[] {1});
await this.messageSession.GetStateAsync();
await this.messageSession.SetStateAsync(new byte[] { });
await this.messageSession.ReceiveAsync();
Assert.True(events.IsEmpty);
}
[Fact]
[DisplayTestMethodName]
async Task SessionHandlerFireEvents()
{
TimeSpan timeout = TimeSpan.FromSeconds(5);
this.queueClient = new QueueClient(TestUtility.NamespaceConnectionString,
TestConstants.SessionNonPartitionedQueueName, ReceiveMode.ReceiveAndDelete, new NoRetry())
{
OperationTimeout = timeout
};
this.queueClient.ServiceBusConnection.OperationTimeout = timeout;
this.queueClient.SessionClient.OperationTimeout = timeout;
Stopwatch sw = Stopwatch.StartNew();
ManualResetEvent processingDone = new ManualResetEvent(false);
this.listener.Enable((name, queueName, arg) => !name.Contains("AcceptMessageSession") &&
!name.Contains("Receive") &&
!name.Contains("Exception"));
var sessionId = Guid.NewGuid().ToString();
var message = new Message
{
MessageId = "messageId",
SessionId = sessionId
};
await this.queueClient.SendAsync(message);
this.queueClient.RegisterSessionHandler((session, msg, ct) =>
{
processingDone.Set();
return Task.CompletedTask;
},
exArgs => Task.CompletedTask);
processingDone.WaitOne(TimeSpan.FromSeconds(maxWaitSec));
Assert.True(events.TryDequeue(out var sendStart));
AssertSendStart(sendStart.eventName, sendStart.payload, sendStart.activity, null);
Assert.True(events.TryDequeue(out var sendStop));
AssertSendStop(sendStop.eventName, sendStop.payload, sendStop.activity, sendStart.activity);
Assert.True(events.TryDequeue(out var processStart));
AssertProcessSessionStart(processStart.eventName, processStart.payload, processStart.activity,
sendStart.activity);
int wait = 0;
while (wait++ < maxWaitSec && events.Count < 1)
{
await Task.Delay(TimeSpan.FromSeconds(1));
}
Assert.True(events.TryDequeue(out var processStop));
AssertProcessSessionStop(processStop.eventName, processStop.payload, processStop.activity,
processStart.activity);
Assert.True(events.IsEmpty);
// workaround for https://github.com/Azure/azure-service-bus-dotnet/issues/372:
// SessionPumpTaskAsync calls AcceptMessageSessionAsync() without cancellation token.
// Even after SessionPump is stopped, this Task may still wait for session during operation timeout
// It may interferee with other tests by acception it's sessions and throwing exceptions.
// So, let's wait for timeout and a bit more to make sure all created tasks are completed
sw.Stop();
var timeToWait = (timeout - sw.Elapsed).TotalMilliseconds + 1000;
if (timeToWait > 0)
{
await Task.Delay((int)timeToWait);
}
}
protected override void Dispose(bool disposing)
{
if (this.disposed)
return;
if (disposing)
{
this.queueClient?.SessionPumpHost?.Close();
this.queueClient?.SessionClient.CloseAsync().Wait(TimeSpan.FromSeconds(maxWaitSec));
this.queueClient?.CloseAsync().Wait(TimeSpan.FromSeconds(maxWaitSec));
this.messageSession?.CloseAsync().Wait(TimeSpan.FromSeconds(maxWaitSec));
this.sessionClient?.CloseAsync().Wait(TimeSpan.FromSeconds(maxWaitSec));
this.messageSender?.CloseAsync().Wait(TimeSpan.FromSeconds(maxWaitSec));
}
this.disposed = true;
base.Dispose(disposing);
}
protected void AssertAcceptMessageSessionStart(string name, object payload, Activity activity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.AcceptMessageSession.Start", name);
this.AssertCommonPayloadProperties(payload);
var sessionId = this.GetPropertyValueFromAnonymousTypeInstance<string>(payload, "SessionId");
Assert.NotNull(activity);
Assert.Null(activity.Parent);
Assert.Equal(sessionId, activity.Tags.Single(t => t.Key == "SessionId").Value);
}
protected void AssertAcceptMessageSessionStop(string name, object payload, Activity activity, Activity acceptActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.AcceptMessageSession.Stop", name);
this.AssertCommonStopPayloadProperties(payload);
this.GetPropertyValueFromAnonymousTypeInstance<string>(payload, "SessionId");
if (acceptActivity != null)
{
Assert.Equal(acceptActivity, activity);
}
}
protected void AssertGetSessionStateStart(string name, object payload, Activity activity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.GetSessionState.Start", name);
this.AssertCommonPayloadProperties(payload);
var sessionId = this.GetPropertyValueFromAnonymousTypeInstance<string>(payload, "SessionId");
Assert.NotNull(activity);
Assert.Null(activity.Parent);
Assert.Equal(sessionId, activity.Tags.Single(t => t.Key == "SessionId").Value);
}
protected void AssertGetSessionStateStop(string name, object payload, Activity activity, Activity getStateActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.GetSessionState.Stop", name);
this.AssertCommonStopPayloadProperties(payload);
this.GetPropertyValueFromAnonymousTypeInstance<string>(payload, "SessionId");
this.GetPropertyValueFromAnonymousTypeInstance<byte[]>(payload, "State");
if (getStateActivity != null)
{
Assert.Equal(getStateActivity, activity);
}
}
protected void AssertSetSessionStateStart(string name, object payload, Activity activity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.SetSessionState.Start", name);
this.AssertCommonPayloadProperties(payload);
var sessionId = this.GetPropertyValueFromAnonymousTypeInstance<string>(payload, "SessionId");
this.GetPropertyValueFromAnonymousTypeInstance<byte[]>(payload, "State");
Assert.NotNull(activity);
Assert.Null(activity.Parent);
Assert.Equal(sessionId, activity.Tags.Single(t => t.Key == "SessionId").Value);
}
protected void AssertSetSessionStateStop(string name, object payload, Activity activity, Activity setStateActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.SetSessionState.Stop", name);
this.AssertCommonStopPayloadProperties(payload);
this.GetPropertyValueFromAnonymousTypeInstance<string>(payload, "SessionId");
this.GetPropertyValueFromAnonymousTypeInstance<byte[]>(payload, "State");
if (setStateActivity != null)
{
Assert.Equal(setStateActivity, activity);
}
}
protected void AssertRenewSessionLockStart(string name, object payload, Activity activity, Activity parentActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.RenewSessionLock.Start", name);
this.AssertCommonPayloadProperties(payload);
var sessionId= this.GetPropertyValueFromAnonymousTypeInstance<string>(payload, "SessionId");
Assert.NotNull(activity);
Assert.Null(activity.Parent);
Assert.Equal(sessionId, activity.Tags.Single(t => t.Key == "SessionId").Value);
}
protected void AssertRenewSessionLockStop(string name, object payload, Activity activity, Activity renewActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.RenewSessionLock.Stop", name);
this.AssertCommonStopPayloadProperties(payload);
this.GetPropertyValueFromAnonymousTypeInstance<string>(payload, "SessionId");
if (renewActivity != null)
{
Assert.Equal(renewActivity, activity);
}
}
protected void AssertProcessSessionStart(string name, object payload, Activity activity, Activity sendActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.ProcessSession.Start", name);
AssertCommonPayloadProperties(payload);
GetPropertyValueFromAnonymousTypeInstance<IMessageSession>(payload, "Session");
var message = GetPropertyValueFromAnonymousTypeInstance<Message>(payload, "Message");
Assert.NotNull(activity);
Assert.Null(activity.Parent);
Assert.Equal(sendActivity.Id, activity.ParentId);
Assert.Equal(sendActivity.Baggage.OrderBy(p => p.Key), activity.Baggage.OrderBy(p => p.Key));
AssertTags(message, activity);
}
protected void AssertProcessSessionStop(string name, object payload, Activity activity, Activity processActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.ProcessSession.Stop", name);
AssertCommonStopPayloadProperties(payload);
GetPropertyValueFromAnonymousTypeInstance<IMessageSession>(payload, "Session");
var message = GetPropertyValueFromAnonymousTypeInstance<Message>(payload, "Message");
if (processActivity != null)
{
Assert.Equal(processActivity, activity);
}
}
}
}

Просмотреть файл

@ -0,0 +1,155 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.Azure.ServiceBus.UnitTests.Diagnostics
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using Xunit;
public class SubscriptionClientDiagnosticsTests : DiagnosticsTests
{
protected override string EntityName => $"{TestConstants.NonPartitionedTopicName}/Subscriptions/{TestConstants.SubscriptionName}";
private SubscriptionClient subscriptionClient;
private bool disposed;
[Fact]
[DisplayTestMethodName]
async Task AddRemoveGetFireEvents()
{
this.subscriptionClient = new SubscriptionClient(
TestUtility.NamespaceConnectionString,
TestConstants.NonPartitionedTopicName,
TestConstants.SubscriptionName,
ReceiveMode.ReceiveAndDelete);
this.listener.Enable((name, queueName, id) => name.Contains("Rule"));
var ruleName = Guid.NewGuid().ToString();
await this.subscriptionClient.AddRuleAsync(ruleName, new TrueFilter());
await this.subscriptionClient.GetRulesAsync();
await this.subscriptionClient.RemoveRuleAsync(ruleName);
Assert.True(this.events.TryDequeue(out var addRuleStart));
AssertAddRuleStart(addRuleStart.eventName, addRuleStart.payload, addRuleStart.activity);
Assert.True(this.events.TryDequeue(out var addRuleStop));
AssertAddRuleStop(addRuleStop.eventName, addRuleStop.payload, addRuleStop.activity, addRuleStart.activity);
Assert.True(this.events.TryDequeue(out var getRulesStart));
AssertGetRulesStart(getRulesStart.eventName, getRulesStart.payload, getRulesStart.activity);
Assert.True(this.events.TryDequeue(out var getRulesStop));
AssertGetRulesStop(getRulesStop.eventName, getRulesStop.payload, getRulesStop.activity, getRulesStart.activity);
Assert.True(this.events.TryDequeue(out var removeRuleStart));
AssertRemoveRuleStart(removeRuleStart.eventName, removeRuleStart.payload, removeRuleStart.activity);
Assert.True(this.events.TryDequeue(out var removeRuleStop));
AssertRemoveRuleStop(removeRuleStop.eventName, removeRuleStop.payload, removeRuleStop.activity, removeRuleStart.activity);
Assert.True(this.events.IsEmpty);
}
[Fact]
[DisplayTestMethodName]
async Task EventsAreNotFiredWhenDiagnosticsIsDisabled()
{
this.subscriptionClient = new SubscriptionClient(
TestUtility.NamespaceConnectionString,
TestConstants.NonPartitionedTopicName,
TestConstants.SubscriptionName,
ReceiveMode.ReceiveAndDelete);
this.listener.Disable();
var ruleName = Guid.NewGuid().ToString();
await this.subscriptionClient.AddRuleAsync(ruleName, new TrueFilter());
await this.subscriptionClient.GetRulesAsync();
await this.subscriptionClient.RemoveRuleAsync(ruleName);
Assert.True(this.events.IsEmpty);
}
protected override void Dispose(bool disposing)
{
if (this.disposed)
return;
if (disposing)
{
this.subscriptionClient?.CloseAsync().Wait(TimeSpan.FromSeconds(maxWaitSec));
}
this.disposed = true;
base.Dispose(disposing);
}
protected void AssertAddRuleStart(string name, object payload, Activity activity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.AddRule.Start", name);
AssertCommonPayloadProperties(payload);
GetPropertyValueFromAnonymousTypeInstance<RuleDescription>(payload, "Rule");
Assert.NotNull(activity);
Assert.Null(activity.Parent);
}
protected void AssertAddRuleStop(string name, object payload, Activity activity, Activity addRuleActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.AddRule.Stop", name);
AssertCommonStopPayloadProperties(payload);
GetPropertyValueFromAnonymousTypeInstance<RuleDescription>(payload, "Rule");
if (addRuleActivity != null)
{
Assert.Equal(addRuleActivity, activity);
}
}
protected void AssertGetRulesStart(string name, object payload, Activity activity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.GetRules.Start", name);
AssertCommonPayloadProperties(payload);
Assert.NotNull(activity);
Assert.Null(activity.Parent);
}
protected void AssertGetRulesStop(string name, object payload, Activity activity, Activity getRulesActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.GetRules.Stop", name);
AssertCommonStopPayloadProperties(payload);
GetPropertyValueFromAnonymousTypeInstance<IEnumerable<RuleDescription>>(payload, "Rules");
if (getRulesActivity != null)
{
Assert.Equal(getRulesActivity, activity);
}
}
protected void AssertRemoveRuleStart(string name, object payload, Activity activity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.RemoveRule.Start", name);
AssertCommonPayloadProperties(payload);
GetPropertyValueFromAnonymousTypeInstance<string>(payload, "RuleName");
Assert.NotNull(activity);
Assert.Null(activity.Parent);
}
protected void AssertRemoveRuleStop(string name, object payload, Activity activity, Activity removeRuleActivity)
{
Assert.Equal("Microsoft.Azure.ServiceBus.RemoveRule.Stop", name);
AssertCommonStopPayloadProperties(payload);
GetPropertyValueFromAnonymousTypeInstance<string>(payload, "RuleName");
if (removeRuleActivity != null)
{
Assert.Equal(removeRuleActivity, activity);
}
}
}
}

Просмотреть файл

@ -17,11 +17,31 @@
<ProjectReference Include="..\..\src\Microsoft.Azure.ServiceBus\Microsoft.Azure.ServiceBus.csproj" /> <ProjectReference Include="..\..\src\Microsoft.Azure.ServiceBus\Microsoft.Azure.ServiceBus.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
<compile Remove="..\..\test\Microsoft.Azure.ServiceBus.UnitTests\ExpectedMessagingExceptionTests.cs" />
<compile Remove="..\..\test\Microsoft.Azure.ServiceBus.UnitTests\OnMessageQueueTests.cs" />
<compile Remove="..\..\test\Microsoft.Azure.ServiceBus.UnitTests\OnSessionQueueTests.cs" />
<compile Remove="..\..\test\Microsoft.Azure.ServiceBus.UnitTests\RetryTests.cs" />
<compile Remove="..\..\test\Microsoft.Azure.ServiceBus.UnitTests\Diagnostics\ExtractActivityTests.cs" />
<compile Remove="..\..\test\Microsoft.Azure.ServiceBus.UnitTests\Diagnostics\QueueClientDiagnosticsTests.cs" />
<compile Remove="..\..\test\Microsoft.Azure.ServiceBus.UnitTests\Diagnostics\SubscriptionClientDiagnosticsTests.cs" />
<compile Remove="..\..\test\Microsoft.Azure.ServiceBus.UnitTests\Diagnostics\SessionDiagnosticsTests.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.0'">
<compile Remove="..\..\test\Microsoft.Azure.ServiceBus.UnitTests\OnMessageTopicSubscriptionTests.cs" />
<compile Remove="..\..\test\Microsoft.Azure.ServiceBus.UnitTests\OnSessionTopicSubscriptionTests.cs" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
<PackageReference Include="System.Net.WebSockets.Client" Version="4.3.1" /> <PackageReference Include="System.Net.WebSockets.Client" Version="4.3.1" />
<PackageReference Include="System.ValueTuple" Version="4.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
<PackageReference Include="xunit" Version="2.2.0" /> <PackageReference Include="xunit" Version="2.2.0" />
<PackageReference Include="PublicApiGenerator" Version="6.5.0" />
<PackageReference Include="ApprovalTests" Version="3.0.13" NoWarn="NU1701" />
<PackageReference Include="ApprovalUtilities" Version="3.0.13" NoWarn="NU1701" />
<PackageReference Include="Microsoft.Extensions.PlatformAbstractions" Version="1.1.0" /> <PackageReference Include="Microsoft.Extensions.PlatformAbstractions" Version="1.1.0" />
</ItemGroup> </ItemGroup>
@ -30,24 +50,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net461'"> <ItemGroup Condition="'$(TargetFramework)' == 'net461'">
<PackageReference Include="ApiApprover"> <PackageReference Include="WindowsAzure.ServiceBus" Version="4.1.2" />
<Version>6.0.0</Version>
</PackageReference>
<PackageReference Include="ApprovalTests">
<Version>3.0.13</Version>
</PackageReference>
<PackageReference Include="ApprovalUtilities">
<Version>3.0.13</Version>
</PackageReference>
<PackageReference Include="Mono.Cecil">
<Version>0.9.6.4</Version>
</PackageReference>
<PackageReference Include="PublicApiGenerator">
<Version>6.0.0</Version>
</PackageReference>
<PackageReference Include="WindowsAzure.ServiceBus">
<Version>4.1.2</Version>
</PackageReference>
</ItemGroup> </ItemGroup>
</Project> </Project>

Просмотреть файл

@ -124,7 +124,7 @@ namespace Microsoft.Azure.ServiceBus.UnitTests
Assert.True(retryExponential.IsServerBusy, "policy1.IsServerBusy is not true"); Assert.True(retryExponential.IsServerBusy, "policy1.IsServerBusy is not true");
} }
[Fact] [Fact(Skip = "Flaky Test in Appveyor, fix and enable back")]
async void RunOperationShouldReturnImmediatelyIfRetryIntervalIsGreaterThanOperationTimeout() async void RunOperationShouldReturnImmediatelyIfRetryIntervalIsGreaterThanOperationTimeout()
{ {
var policy = RetryPolicy.Default; var policy = RetryPolicy.Default;

Просмотреть файл

@ -166,6 +166,7 @@ namespace Microsoft.Azure.ServiceBus.UnitTests
internal async Task ReceiveShouldReturnNoLaterThanServerWaitTimeTestCase(IMessageSender messageSender, IMessageReceiver messageReceiver, int messageCount) internal async Task ReceiveShouldReturnNoLaterThanServerWaitTimeTestCase(IMessageSender messageSender, IMessageReceiver messageReceiver, int messageCount)
{ {
var timer = Stopwatch.StartNew(); var timer = Stopwatch.StartNew();
TestUtility.Log($"starting Receive");
var message = await messageReceiver.ReceiveAsync(TimeSpan.FromSeconds(2)); var message = await messageReceiver.ReceiveAsync(TimeSpan.FromSeconds(2));
timer.Stop(); timer.Stop();
@ -175,6 +176,7 @@ namespace Microsoft.Azure.ServiceBus.UnitTests
// Ensuring total time taken is less than 60 seconds, which is the default timeout for receive. // Ensuring total time taken is less than 60 seconds, which is the default timeout for receive.
// Keeping the value of 40 to avoid flakiness in test infrastructure which may lead to extended time taken. // Keeping the value of 40 to avoid flakiness in test infrastructure which may lead to extended time taken.
// Todo: Change this value to a lower number once test infra is performant. // Todo: Change this value to a lower number once test infra is performant.
TestUtility.Log($"Elapsed Milliseconds: {timer.Elapsed.TotalMilliseconds}");
Assert.True(timer.Elapsed.TotalSeconds < 40); Assert.True(timer.Elapsed.TotalSeconds < 40);
} }

Просмотреть файл

@ -6,6 +6,7 @@ namespace Microsoft.Azure.ServiceBus.UnitTests
using System; using System;
using System.Text; using System.Text;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Core; using Core;
using Xunit; using Xunit;
@ -184,7 +185,7 @@ namespace Microsoft.Azure.ServiceBus.UnitTests
} }
} }
[Fact] [Fact(Skip = "Flaky Test in Appveyor, fix and enable back")]
[DisplayTestMethodName] [DisplayTestMethodName]
public async Task WaitingReceiveShouldReturnImmediatelyWhenReceiverIsClosed() public async Task WaitingReceiveShouldReturnImmediatelyWhenReceiverIsClosed()
{ {
@ -215,8 +216,24 @@ namespace Microsoft.Azure.ServiceBus.UnitTests
} }
TestUtility.Log("Waiting for maximum 10 Secs"); TestUtility.Log("Waiting for maximum 10 Secs");
var waitingTask = Task.Delay(10000); bool receiverReturnedInTime = false;
Assert.Equal(quickTask, await Task.WhenAny(quickTask, waitingTask)); using (var timeoutCancellationTokenSource = new CancellationTokenSource())
{
var completedTask = await Task.WhenAny(quickTask, Task.Delay(10000, timeoutCancellationTokenSource.Token));
if (completedTask == quickTask)
{
timeoutCancellationTokenSource.Cancel();
receiverReturnedInTime = true;
TestUtility.Log("The Receiver closed in time.");
}
else
{
TestUtility.Log("The Receiver did not close in time.");
}
}
Assert.True(receiverReturnedInTime);
} }
[Fact] [Fact]

Просмотреть файл

@ -0,0 +1,44 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.Azure.ServiceBus.UnitTests
{
using System;
using System.Threading.Tasks;
using Microsoft.Azure.ServiceBus.Primitives;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Xunit;
public class TokenProviderTests : SenderReceiverClientTestBase
{
/// <summary>
/// This test is for manual only purpose. Fill in the tenant-id, app-id and app-secret before running.
/// </summary>
/// <returns></returns>
[Fact]
[DisplayTestMethodName]
async Task UseITokenProviderWithAad()
{
var tenantId = "";
var aadAppId = "";
var aadAppSecret = "";
if (string.IsNullOrEmpty(tenantId))
{
TestUtility.Log($"Skipping test during scheduled runs.");
return;
}
var authContext = new AuthenticationContext($"https://login.windows.net/{tenantId}");
var cc = new ClientCredential(aadAppId, aadAppSecret);
var tokenProvider = TokenProvider.CreateAadTokenProvider(authContext, cc);
// Create new client with updated connection string.
var csb = new ServiceBusConnectionStringBuilder(TestUtility.NamespaceConnectionString);
var queueClient = new QueueClient(csb.Endpoint, csb.EntityPath, tokenProvider);
// Send and receive messages.
await this.PeekLockTestCase(queueClient.InnerSender, queueClient.InnerReceiver, 10);
}
}
}