Merge pull request #669 from Azure/release-3.4.0

Release 3.4.0
This commit is contained in:
Sean Feldman 2019-03-20 17:05:19 -07:00 коммит произвёл GitHub
Родитель e3b417d008 41551d7afa
Коммит ff1093d6c5
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
17 изменённых файлов: 219 добавлений и 39 удалений

12
.github/ISSUE_TEMPLATE.md поставляемый
Просмотреть файл

@ -1,12 +0,0 @@
## Actual Behavior
1.
2.
## Expected Behavior
1.
2.
## Versions
- OS platform and version:
- .NET Version:
- NuGet package version or commit ID:

22
.github/ISSUE_TEMPLATE/bug_report.md поставляемый Normal file
Просмотреть файл

@ -0,0 +1,22 @@
---
name: Bug report
about: Report a bug to help us improve
title: ''
labels: ''
assignees: ''
---
## Actual Behavior
1.
2.
## Expected Behavior
1.
2.
## Versions
- OS platform and version:
- .NET Version:
- NuGet package version or commit ID:

24
.github/ISSUE_TEMPLATE/feature_request.md поставляемый Normal file
Просмотреть файл

@ -0,0 +1,24 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. E.g. “Im always frustrated when [...]”
**Describe the solution youd like**
A clear and concise description of what you want to happen.
**Describe alternatives youve considered**
A clear and concise description of any alternative solutions or features youve considered.
**Additional context**
Add any other context or screenshots about the feature request here.

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

@ -4,6 +4,7 @@
namespace Microsoft.Azure.ServiceBus.Amqp
{
using System;
using System.Net;
using Microsoft.Azure.Amqp;
using Microsoft.Azure.Amqp.Sasl;
using Microsoft.Azure.Amqp.Transport;
@ -101,7 +102,8 @@ namespace Microsoft.Azure.ServiceBus.Amqp
public static TransportSettings CreateWebSocketTransportSettings(
string networkHost,
string hostName,
int port)
int port,
IWebProxy proxy)
{
var uriBuilder = new UriBuilder(
WebSocketConstants.WebSocketSecureScheme,
@ -112,7 +114,8 @@ namespace Microsoft.Azure.ServiceBus.Amqp
{
Uri = uriBuilder.Uri,
ReceiveBufferSize = AmqpConstants.TransportBufferSize,
SendBufferSize = AmqpConstants.TransportBufferSize
SendBufferSize = AmqpConstants.TransportBufferSize,
Proxy = proxy
};
TransportSettings tpSettings = webSocketTransportSettings;

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

@ -237,6 +237,11 @@ namespace Microsoft.Azure.ServiceBus.Core
this.ThrowIfClosed();
var count = MessageSender.ValidateMessages(messageList);
if (count <= 0)
{
return;
}
MessagingEventSource.Log.MessageSendStart(this.ClientId, count);
bool isDiagnosticSourceEnabled = ServiceBusDiagnosticSource.IsEnabled();

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

@ -18,7 +18,6 @@ namespace Microsoft.Azure.ServiceBus.Management
private HttpClient httpClient;
private readonly string endpointFQDN;
private readonly ITokenProvider tokenProvider;
private readonly TimeSpan operationTimeout;
private readonly int port;
private readonly string clientId;
@ -48,14 +47,13 @@ namespace Microsoft.Azure.ServiceBus.Management
/// <param name="tokenProvider">Token provider which will generate security tokens for authorization.</param>
public ManagementClient(ServiceBusConnectionStringBuilder connectionStringBuilder, ITokenProvider tokenProvider = default)
{
this.httpClient = new HttpClient();
this.httpClient = new HttpClient { Timeout = connectionStringBuilder.OperationTimeout };
this.endpointFQDN = connectionStringBuilder.Endpoint;
this.tokenProvider = tokenProvider ?? CreateTokenProvider(connectionStringBuilder);
this.operationTimeout = Constants.DefaultOperationTimeout;
this.port = GetPort(connectionStringBuilder.Endpoint);
this.clientId = nameof(ManagementClient) + Guid.NewGuid().ToString("N").Substring(0, 6);
MessagingEventSource.Log.ManagementClientCreated(this.clientId, this.operationTimeout.TotalSeconds, this.tokenProvider.ToString());
MessagingEventSource.Log.ManagementClientCreated(this.clientId, this.httpClient.Timeout.TotalSeconds, this.tokenProvider.ToString());
}
public static HttpRequestMessage CloneRequest(HttpRequestMessage req)

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

@ -57,11 +57,19 @@ namespace Microsoft.Azure.ServiceBus.Management
case "Alias":
nsInfo.Alias = element.Value;
break;
case "MessagingUnits":
int.TryParse(element.Value, out var units);
nsInfo.MessagingUnits = units;
break;
case "NamespaceType":
if (Enum.TryParse<NamespaceType>(element.Value, out var nsType))
{
nsInfo.NamespaceType = nsType;
}
else if (element.Value == "Messaging") // TODO: workaround till next major as it's a breaking change
{
nsInfo.NamespaceType = NamespaceType.ServiceBus;
}
else
{
nsInfo.NamespaceType = NamespaceType.Others;

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

@ -268,7 +268,7 @@ namespace Microsoft.Azure.ServiceBus
/// <summary>
/// Gets the total size of the message body in bytes.
/// </summary>
public long Size => Body.Length;
public long Size => this.Body != null ? this.Body.Length : 0;
/// <summary>
/// Gets the "user properties" bag, which can be used for custom message metadata.

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

@ -23,14 +23,14 @@
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>CS1591;CS1573</NoWarn>
<NoWarn>CS1591;CS1573;NU5125</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.Amqp" Version="[2.3.7, 3.0.0)" />
<PackageReference Include="Microsoft.Azure.Amqp" Version="[2.4.1, 3.0.0)" />
<PackageReference Include="Microsoft.Azure.Services.AppAuthentication" Version="[1.0.1, 2.0.0)" />
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="[3.17.2, 5.0.0)" />
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="4.4.1" />
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="4.5.1" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="[5.2.2, 6.0.0)" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-18618-05" PrivateAssets="All" />
</ItemGroup>

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

@ -4,6 +4,7 @@
namespace Microsoft.Azure.ServiceBus
{
using System;
using System.Net;
using System.Threading.Tasks;
using Microsoft.Azure.Amqp;
using Microsoft.Azure.Amqp.Framing;
@ -37,7 +38,7 @@ namespace Microsoft.Azure.ServiceBus
/// <param name="namespaceConnectionString">Namespace connection string</param>
/// <remarks>It is the responsibility of the user to close the connection after use through <see cref="CloseAsync"/></remarks>
public ServiceBusConnection(string namespaceConnectionString)
: this(namespaceConnectionString, Constants.DefaultOperationTimeout, RetryPolicy.Default)
: this(namespaceConnectionString, RetryPolicy.Default)
{
}
@ -45,11 +46,10 @@ namespace Microsoft.Azure.ServiceBus
/// Creates a new connection to service bus.
/// </summary>
/// <param name="namespaceConnectionString">Namespace connection string.</param>
/// <param name="operationTimeout">Duration after which individual operations will timeout.</param>
/// <param name="retryPolicy">Retry policy for operations. Defaults to <see cref="RetryPolicy.Default"/></param>
/// <remarks>It is the responsibility of the user to close the connection after use through <see cref="CloseAsync"/></remarks>
public ServiceBusConnection(string namespaceConnectionString, TimeSpan operationTimeout, RetryPolicy retryPolicy = null)
: this(operationTimeout, retryPolicy)
public ServiceBusConnection(string namespaceConnectionString, RetryPolicy retryPolicy = null)
: this(retryPolicy)
{
if (string.IsNullOrWhiteSpace(namespaceConnectionString))
{
@ -65,6 +65,33 @@ namespace Microsoft.Azure.ServiceBus
this.InitializeConnection(serviceBusConnectionStringBuilder);
}
/// <summary>
/// Creates a new connection to service bus.
/// </summary>
/// <param name="namespaceConnectionString">Namespace connection string.</param>
/// <param name="operationTimeout">Duration after which individual operations will timeout.</param>
/// <param name="retryPolicy">Retry policy for operations. Defaults to <see cref="RetryPolicy.Default"/></param>
/// <remarks>It is the responsibility of the user to close the connection after use through <see cref="CloseAsync"/></remarks>
[Obsolete("This constructor is obsolete. Use ServiceBusConnection(string namespaceConnectionString, RetryPolicy retryPolicy) constructor instead, providing operationTimeout in the connection string.")]
public ServiceBusConnection(string namespaceConnectionString, TimeSpan operationTimeout, RetryPolicy retryPolicy = null)
: this(retryPolicy)
{
if (string.IsNullOrWhiteSpace(namespaceConnectionString))
{
throw Fx.Exception.ArgumentNullOrWhiteSpace(nameof(namespaceConnectionString));
}
var serviceBusConnectionStringBuilder = new ServiceBusConnectionStringBuilder(namespaceConnectionString);
if (!string.IsNullOrWhiteSpace(serviceBusConnectionStringBuilder.EntityPath))
{
throw Fx.Exception.Argument(nameof(namespaceConnectionString), "NamespaceConnectionString should not contain EntityPath.");
}
this.InitializeConnection(serviceBusConnectionStringBuilder);
// operationTimeout argument explicitly provided by caller should take precedence over OperationTimeout found in the connection string.
this.OperationTimeout = operationTimeout;
}
/// <summary>
/// Creates a new connection to service bus.
/// </summary>
@ -72,7 +99,7 @@ namespace Microsoft.Azure.ServiceBus
/// <param name="transportType">Transport type.</param>
/// <param name="retryPolicy">Retry policy for operations. Defaults to <see cref="RetryPolicy.Default"/></param>
public ServiceBusConnection(string endpoint, TransportType transportType, RetryPolicy retryPolicy = null)
: this(Constants.DefaultOperationTimeout, retryPolicy)
: this(retryPolicy)
{
if (string.IsNullOrWhiteSpace(endpoint))
{
@ -88,9 +115,8 @@ namespace Microsoft.Azure.ServiceBus
this.InitializeConnection(serviceBusConnectionStringBuilder);
}
internal ServiceBusConnection(TimeSpan operationTimeout, RetryPolicy retryPolicy = null)
internal ServiceBusConnection(RetryPolicy retryPolicy = null)
{
this.OperationTimeout = operationTimeout;
this.RetryPolicy = retryPolicy ?? RetryPolicy.Default;
this.syncLock = new object();
}
@ -193,6 +219,7 @@ namespace Microsoft.Azure.ServiceBus
this.TokenProvider = new SharedAccessSignatureTokenProvider(builder.SasKeyName, builder.SasKey);
}
this.OperationTimeout = builder.OperationTimeout;
this.TransportType = builder.TransportType;
this.ConnectionManager = new FaultTolerantAmqpObject<AmqpConnection>(this.CreateConnectionAsync, CloseConnection);
this.TransactionController = new FaultTolerantAmqpObject<Controller>(this.CreateControllerAsync, CloseController);
@ -283,7 +310,8 @@ namespace Microsoft.Azure.ServiceBus
return AmqpConnectionHelper.CreateWebSocketTransportSettings(
networkHost: networkHost,
hostName: hostName,
port: port);
port: port,
proxy: WebRequest.DefaultWebProxy);
}
return AmqpConnectionHelper.CreateTcpTransportSettings(

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

@ -5,6 +5,7 @@ namespace Microsoft.Azure.ServiceBus
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using Primitives;
@ -24,6 +25,8 @@ namespace Microsoft.Azure.ServiceBus
const string EntityPathConfigName = "EntityPath";
const string TransportTypeConfigName = "TransportType";
const string OperationTimeoutConfigName = "OperationTimeout";
string entityPath, sasKeyName, sasKey, sasToken, endpoint;
/// <summary>
@ -212,6 +215,12 @@ namespace Microsoft.Azure.ServiceBus
/// </summary>
public TransportType TransportType { get; set; }
/// <summary>
/// Duration after which individual operations will timeout.
/// </summary>
/// <remarks>Defaults to 1 minute.</remarks>
public TimeSpan OperationTimeout { get; set; } = Constants.DefaultOperationTimeout;
internal Dictionary<string, string> ConnectionStringProperties = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
/// <summary>
@ -243,7 +252,12 @@ namespace Microsoft.Azure.ServiceBus
if (this.TransportType != TransportType.Amqp)
{
connectionStringBuilder.Append($"{TransportTypeConfigName}{KeyValueSeparator}{this.TransportType}");
connectionStringBuilder.Append($"{TransportTypeConfigName}{KeyValueSeparator}{this.TransportType}{KeyValuePairDelimiter}");
}
if (this.OperationTimeout != Constants.DefaultOperationTimeout)
{
connectionStringBuilder.Append($"{OperationTimeoutConfigName}{KeyValueSeparator}{this.OperationTimeout}{KeyValuePairDelimiter}");
}
return connectionStringBuilder.ToString().Trim(';');
@ -319,6 +333,31 @@ namespace Microsoft.Azure.ServiceBus
this.TransportType = transportType;
}
}
else if (key.Equals(OperationTimeoutConfigName, StringComparison.OrdinalIgnoreCase))
{
if (int.TryParse(value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out var timeoutInSeconds))
{
this.OperationTimeout = TimeSpan.FromSeconds(timeoutInSeconds);
}
else if (TimeSpan.TryParse(value, NumberFormatInfo.InvariantInfo, out var operationTimeout))
{
this.OperationTimeout = operationTimeout;
}
else
{
throw Fx.Exception.Argument(nameof(connectionString), $"The {OperationTimeoutConfigName} ({value}) format is invalid. It must be an integer representing a number of seconds.");
}
if (this.OperationTimeout.TotalMilliseconds <= 0)
{
throw Fx.Exception.Argument(nameof(connectionString), $"The {OperationTimeoutConfigName} ({value}) must be greater than zero.");
}
if (this.OperationTimeout.TotalHours >= 1)
{
throw Fx.Exception.Argument(nameof(connectionString), $"The {OperationTimeoutConfigName} ({value}) must be smaller than one hour.");
}
}
else
{
ConnectionStringProperties[key] = value;

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

@ -309,6 +309,10 @@ namespace Microsoft.Azure.ServiceBus
{
public ServiceBusConnection(Microsoft.Azure.ServiceBus.ServiceBusConnectionStringBuilder connectionStringBuilder) { }
public ServiceBusConnection(string namespaceConnectionString) { }
public ServiceBusConnection(string namespaceConnectionString, Microsoft.Azure.ServiceBus.RetryPolicy retryPolicy = null) { }
[System.ObsoleteAttribute("This constructor is obsolete. Use ServiceBusConnection(string namespaceConnection" +
"String, RetryPolicy retryPolicy) constructor instead, providing operationTimeout" +
" in the connection string.")]
public ServiceBusConnection(string namespaceConnectionString, System.TimeSpan operationTimeout, Microsoft.Azure.ServiceBus.RetryPolicy retryPolicy = null) { }
public ServiceBusConnection(string endpoint, Microsoft.Azure.ServiceBus.TransportType transportType, Microsoft.Azure.ServiceBus.RetryPolicy retryPolicy = null) { }
public System.Uri Endpoint { get; set; }
@ -329,6 +333,7 @@ namespace Microsoft.Azure.ServiceBus
public ServiceBusConnectionStringBuilder(string endpoint, string entityPath, string sharedAccessSignature, Microsoft.Azure.ServiceBus.TransportType transportType) { }
public string Endpoint { get; set; }
public string EntityPath { get; set; }
public System.TimeSpan OperationTimeout { get; set; }
public string SasKey { get; set; }
public string SasKeyName { get; set; }
public string SasToken { get; set; }

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

@ -640,6 +640,7 @@ namespace Microsoft.Azure.ServiceBus.UnitTests.Management
var nsInfo = await client.GetNamespaceInfoAsync();
Assert.NotNull(nsInfo);
Assert.Equal(MessagingSku.Standard, nsInfo.MessagingSku); // Most CI systems generally use standard, hence this check just to ensure the API is working.
Assert.Equal(NamespaceType.ServiceBus, nsInfo.NamespaceType); // Common namespace type used for testing is messaging.
}
public void Dispose()

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

@ -36,14 +36,14 @@
<compile Remove="..\..\test\Microsoft.Azure.ServiceBus.UnitTests\Diagnostics\SubscriptionClientDiagnosticsTests.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.6.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="System.Net.WebSockets.Client" Version="4.3.2" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="PublicApiGenerator" Version="8.1.0" />
<PackageReference Include="ApprovalTests" Version="3.0.18" NoWarn="NU1701" />
<PackageReference Include="ApprovalUtilities" Version="3.0.18" NoWarn="NU1701" />
<PackageReference Include="ApprovalTests" Version="3.0.21" NoWarn="NU1701" />
<PackageReference Include="ApprovalUtilities" Version="3.0.21" NoWarn="NU1701" />
<PackageReference Include="Microsoft.Extensions.PlatformAbstractions" Version="1.1.0" />
</ItemGroup>
@ -56,7 +56,7 @@
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
<PackageReference Include="WindowsAzure.ServiceBus" Version="4.1.2" />
<PackageReference Include="WindowsAzure.ServiceBus" Version="5.1.0" />
</ItemGroup>

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

@ -459,5 +459,27 @@ namespace Microsoft.Azure.ServiceBus.UnitTests
await receiver.CloseAsync().ConfigureAwait(false);
}
}
[Theory]
[InlineData(TestConstants.NonPartitionedQueueName)]
[DisplayTestMethodName]
async Task MessageSenderShouldNotThrowWhenSendingEmptyCollection(string queueName)
{
var sender = new MessageSender(TestUtility.NamespaceConnectionString, queueName);
var receiver = new MessageReceiver(TestUtility.NamespaceConnectionString, queueName, ReceiveMode.ReceiveAndDelete);
try
{
await sender.SendAsync(new List<Message>());
var message = await receiver.ReceiveAsync(TimeSpan.FromSeconds(3));
Assert.True(message == null, "Expected not to find any messages, but a message was received.");
}
finally
{
await sender.CloseAsync();
await receiver.CloseAsync();
}
}
}
}

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

@ -75,6 +75,13 @@ namespace Microsoft.Azure.ServiceBus.UnitTests
csBuilder.EntityPath = "myQ";
Assert.Equal("Endpoint=amqps://contoso.servicebus.windows.net;EntityPath=myQ", csBuilder.ToString());
csBuilder.EntityPath = "";
csBuilder.TransportType = TransportType.AmqpWebSockets;
Assert.Equal("Endpoint=amqps://contoso.servicebus.windows.net;TransportType=AmqpWebSockets", csBuilder.ToString());
csBuilder.OperationTimeout = TimeSpan.FromSeconds(42);
Assert.Equal("Endpoint=amqps://contoso.servicebus.windows.net;TransportType=AmqpWebSockets;OperationTimeout=00:00:42", csBuilder.ToString());
}
[Fact]
@ -126,6 +133,36 @@ namespace Microsoft.Azure.ServiceBus.UnitTests
Assert.Equal(TransportType.Amqp, csBuilder.TransportType);
}
[Fact]
void ConnectionStringBuilderShouldParseOperationTimeoutAsInteger()
{
var csBuilder = new ServiceBusConnectionStringBuilder("Endpoint=sb://contoso.servicebus.windows.net;SharedAccessKeyName=keyname;SharedAccessKey=key;OperationTimeout=120");
Assert.Equal(TimeSpan.FromMinutes(2), csBuilder.OperationTimeout);
}
[Fact]
void ConnectionStringBuilderShouldParseOperationTimeoutAsTimeSpan()
{
var csBuilder = new ServiceBusConnectionStringBuilder("Endpoint=sb://contoso.servicebus.windows.net;SharedAccessKeyName=keyname;SharedAccessKey=key;OperationTimeout=00:12:34");
Assert.Equal(TimeSpan.FromMinutes(12).Add(TimeSpan.FromSeconds(34)), csBuilder.OperationTimeout);
}
[Fact]
void ConnectionStringBuilderOperationTimeoutShouldDefaultToOneMinute()
{
var csBuilder = new ServiceBusConnectionStringBuilder("Endpoint=sb://contoso.servicebus.windows.net;SharedAccessKeyName=keyname;SharedAccessKey=key");
Assert.Equal(Constants.DefaultOperationTimeout, csBuilder.OperationTimeout);
}
[Fact]
void ConnectionStringBuilderShouldThrowForInvalidOperationTimeout()
{
var exception = Assert.Throws<ArgumentException>(() => new ServiceBusConnectionStringBuilder("Endpoint=sb://contoso.servicebus.windows.net;SharedAccessKeyName=keyname;SharedAccessKey=key;OperationTimeout=x"));
Assert.Equal("connectionString", exception.ParamName);
Assert.Contains("OperationTimeout", exception.Message);
Assert.Contains("(x)", exception.Message);
}
[Fact]
void ConnectionStringBuilderShouldParseToken()
{