Added White-/Blacklist Attributes, DateOnly/TimeOnly converters, TcpClientMoq
This commit is contained in:
@@ -38,11 +38,11 @@
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.1" />
|
||||
<PackageReference Include="Unclassified.DeepConvert" Version="1.3.0" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.2">
|
||||
<PackageReference Include="Unclassified.DeepConvert" Version="1.4.0" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
103
AMWD.Common.AspNetCore/Attributes/IPBlacklistAttribute.cs
Normal file
103
AMWD.Common.AspNetCore/Attributes/IPBlacklistAttribute.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace AMWD.Common.AspNetCore.Attributes
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements an IP filter. The defined addresses are blocked.
|
||||
/// </summary>
|
||||
public class IPBlacklistAttribute : ActionFilterAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether local (localhost) access is blocked (Default: false).
|
||||
/// </summary>
|
||||
public bool RestrictLocalAccess { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a configuration key where the blocked IP addresses are defined.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// JSON configuration example:<br/>
|
||||
/// {<br/>
|
||||
/// "ConfigurationKey": [<br/>
|
||||
/// "10.0.0.0/8",<br/>
|
||||
/// "172.16.0.0/12",<br/>
|
||||
/// "fd00:123:abc::13"<br/>
|
||||
/// ]<br/>
|
||||
/// }
|
||||
/// </remarks>
|
||||
public string ConfigurationKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a comma separated list of blocked IP addresses.
|
||||
/// </summary>
|
||||
public string RestrictedIpAddresses { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnActionExecuting(ActionExecutingContext context)
|
||||
{
|
||||
base.OnActionExecuting(context);
|
||||
|
||||
if (!RestrictLocalAccess && context.HttpContext.IsLocalRequest())
|
||||
return;
|
||||
|
||||
var remoteIpAddress = context.HttpContext.GetRemoteIpAddress();
|
||||
if (!string.IsNullOrWhiteSpace(RestrictedIpAddresses))
|
||||
{
|
||||
string[] ipAddresses = RestrictedIpAddresses.Split(',');
|
||||
foreach (string ipAddress in ipAddresses)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(ipAddress))
|
||||
continue;
|
||||
|
||||
if (MatchesIpAddress(ipAddress, remoteIpAddress))
|
||||
{
|
||||
context.Result = new ForbidResult();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var configuration = context.HttpContext.RequestServices.GetService<IConfiguration>();
|
||||
if (!string.IsNullOrWhiteSpace(ConfigurationKey) && configuration != null)
|
||||
{
|
||||
var section = configuration.GetSection(ConfigurationKey);
|
||||
if (!section.Exists())
|
||||
return;
|
||||
|
||||
foreach (var child in section.GetChildren())
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(child.Value))
|
||||
continue;
|
||||
|
||||
if (MatchesIpAddress(child.Value, remoteIpAddress))
|
||||
{
|
||||
context.Result = new ForbidResult();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool MatchesIpAddress(string configIpAddress, IPAddress remoteIpAddress)
|
||||
{
|
||||
if (configIpAddress.Contains('/'))
|
||||
{
|
||||
string[] ipNetworkParts = configIpAddress.Split('/');
|
||||
var ip = IPAddress.Parse(ipNetworkParts.First());
|
||||
int prefix = int.Parse(ipNetworkParts.Last());
|
||||
|
||||
var net = new IPNetwork(ip, prefix);
|
||||
return net.Contains(remoteIpAddress);
|
||||
}
|
||||
|
||||
return IPAddress.Parse(configIpAddress).Equals(remoteIpAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
102
AMWD.Common.AspNetCore/Attributes/IPWhitelistAttribute.cs
Normal file
102
AMWD.Common.AspNetCore/Attributes/IPWhitelistAttribute.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace AMWD.Common.AspNetCore.Attributes
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements an IP filter. Only defined addresses are allowed to access.
|
||||
/// </summary>
|
||||
public class IPWhitelistAttribute : ActionFilterAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether local (localhost) access is granted (Default: true).
|
||||
/// </summary>
|
||||
public bool AllowLocalAccess { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a configuration key where the allowed IP addresses are allowed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// JSON configuration example:<br/>
|
||||
/// {<br/>
|
||||
/// "ConfigurationKey": [<br/>
|
||||
/// "10.0.0.0/8",<br/>
|
||||
/// "172.16.0.0/12",<br/>
|
||||
/// "fd00:123:abc::13"<br/>
|
||||
/// ]<br/>
|
||||
/// }
|
||||
/// </remarks>
|
||||
public string ConfigurationKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a comma separated list of allowed IP addresses.
|
||||
/// </summary>
|
||||
public string AllowedIpAddresses { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnActionExecuting(ActionExecutingContext context)
|
||||
{
|
||||
base.OnActionExecuting(context);
|
||||
|
||||
if (AllowLocalAccess && context.HttpContext.IsLocalRequest())
|
||||
return;
|
||||
|
||||
var remoteIpAddress = context.HttpContext.GetRemoteIpAddress();
|
||||
if (!string.IsNullOrWhiteSpace(AllowedIpAddresses))
|
||||
{
|
||||
string[] ipAddresses = AllowedIpAddresses.Split(',');
|
||||
foreach (string ipAddress in ipAddresses)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(ipAddress))
|
||||
continue;
|
||||
|
||||
if (MatchesIpAddress(ipAddress, remoteIpAddress))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var configuration = context.HttpContext.RequestServices.GetService<IConfiguration>();
|
||||
if (!string.IsNullOrWhiteSpace(ConfigurationKey) && configuration != null)
|
||||
{
|
||||
var section = configuration.GetSection(ConfigurationKey);
|
||||
if (!section.Exists())
|
||||
{
|
||||
context.Result = new ForbidResult();
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var child in section.GetChildren())
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(child.Value))
|
||||
continue;
|
||||
|
||||
if (MatchesIpAddress(child.Value, remoteIpAddress))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
context.Result = new ForbidResult();
|
||||
}
|
||||
|
||||
private static bool MatchesIpAddress(string configIpAddress, IPAddress remoteIpAddress)
|
||||
{
|
||||
if (configIpAddress.Contains('/'))
|
||||
{
|
||||
string[] ipNetworkParts = configIpAddress.Split('/');
|
||||
var ip = IPAddress.Parse(ipNetworkParts.First());
|
||||
int prefix = int.Parse(ipNetworkParts.Last());
|
||||
|
||||
var net = new IPNetwork(ip, prefix);
|
||||
return net.Contains(remoteIpAddress);
|
||||
}
|
||||
|
||||
return IPAddress.Parse(configIpAddress).Equals(remoteIpAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Http
|
||||
/// <summary>
|
||||
/// Retrieves the antiforgery token.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The web context.</param>
|
||||
/// <param name="httpContext">The current <see cref="HttpContext"/>.</param>
|
||||
/// <returns>Name and value of the token.</returns>
|
||||
public static (string Name, string Value) GetAntiforgeryToken(this HttpContext httpContext)
|
||||
{
|
||||
@@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Http
|
||||
/// <summary>
|
||||
/// Returns the remote ip address.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The web context.</param>
|
||||
/// <param name="httpContext">The current <see cref="HttpContext"/>.</param>
|
||||
/// <param name="headerName">The name of the header to resolve the <see cref="IPAddress"/> when behind a proxy (Default: X-Forwarded-For).</param>
|
||||
/// <returns>The ip address of the client.</returns>
|
||||
public static IPAddress GetRemoteIpAddress(this HttpContext httpContext, string headerName = "X-Forwarded-For")
|
||||
@@ -39,10 +39,22 @@ namespace Microsoft.AspNetCore.Http
|
||||
return remote;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the request was made locally.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The current <see cref="HttpContext"/>.</param>
|
||||
/// <param name="headerName">The name of the header to resolve the <see cref="IPAddress"/> when behind a proxy (Default: X-Forwarded-For).</param>
|
||||
/// <returns></returns>
|
||||
public static bool IsLocalRequest(this HttpContext httpContext, string headerName = "X-Forwarded-For")
|
||||
{
|
||||
var remoteIpAddress = httpContext.GetRemoteIpAddress(headerName);
|
||||
return httpContext.Connection.LocalIpAddress.Equals(remoteIpAddress);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to retrieve the return url.
|
||||
/// </summary>
|
||||
/// <param name="httpContext"></param>
|
||||
/// <param name="httpContext">The current <see cref="HttpContext"/>.</param>
|
||||
/// <returns></returns>
|
||||
public static string GetReturnUrl(this HttpContext httpContext)
|
||||
{
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.2">
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
#if NET6_0_OR_GREATER
|
||||
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace AMWD.Common.EntityFrameworkCore.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the conversion from a <see cref="DateOnly"/> object to a <see cref="DateTime"/> which can be handled by the database engine.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// As of 2022-06-04 only required for Microsoft SQL server on .NET 6.0.
|
||||
/// </remarks>
|
||||
public class DateOnlyConverter : ValueConverter<DateOnly, DateTime>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DateOnlyConverter"/> class.
|
||||
/// </summary>
|
||||
public DateOnlyConverter()
|
||||
: base(
|
||||
d => d.ToDateTime(TimeOnly.MinValue),
|
||||
d => DateOnly.FromDateTime(d)
|
||||
)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
#if NET6_0_OR_GREATER
|
||||
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace AMWD.Common.EntityFrameworkCore.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the conversion from a nullable <see cref="DateOnly"/> object to a nullable <see cref="DateTime"/> which can be handled by the database engine.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// As of 2022-06-04 only required for Microsoft SQL server on .NET 6.0.
|
||||
/// </remarks>
|
||||
public class NullableDateOnlyConverter : ValueConverter<DateOnly?, DateTime?>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NullableDateOnlyConverter"/> class.
|
||||
/// </summary>
|
||||
public NullableDateOnlyConverter()
|
||||
: base(
|
||||
d => d == null ? null : new DateTime?(d.Value.ToDateTime(TimeOnly.MinValue)),
|
||||
d => d == null ? null : new DateOnly?(DateOnly.FromDateTime(d.Value))
|
||||
)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
#if NET6_0_OR_GREATER
|
||||
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace AMWD.Common.EntityFrameworkCore.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the conversion from a nullable <see cref="TimeOnly"/> object to a nullable <see cref="TimeSpan"/> which can be handled by the database engine.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// As of 2022-06-04 only required for Microsoft SQL server on .NET 6.0.
|
||||
/// </remarks>
|
||||
public class NullableTimeOnlyConverter : ValueConverter<TimeOnly?, TimeSpan?>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NullableTimeOnlyConverter"/> class.
|
||||
/// </summary>
|
||||
public NullableTimeOnlyConverter()
|
||||
: base(
|
||||
d => d == null ? null : new TimeSpan?(d.Value.ToTimeSpan()),
|
||||
d => d == null ? null : new TimeOnly?(TimeOnly.FromTimeSpan(d.Value))
|
||||
)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
#if NET6_0_OR_GREATER
|
||||
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace AMWD.Common.EntityFrameworkCore.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the conversion from a <see cref="TimeOnly"/> object to a <see cref="TimeSpan"/> which can be handled by the database engine.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// As of 2022-06-04 only required for Microsoft SQL server on .NET 6.0.
|
||||
/// </remarks>
|
||||
public class TimeOnlyConverter : ValueConverter<TimeOnly, TimeSpan>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TimeOnlyConverter"/> class.
|
||||
/// </summary>
|
||||
public TimeOnlyConverter()
|
||||
: base(
|
||||
t => t.ToTimeSpan(),
|
||||
t => TimeOnly.FromTimeSpan(t)
|
||||
)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
#if NET6_0_OR_GREATER
|
||||
|
||||
using System;
|
||||
using AMWD.Common.EntityFrameworkCore.Converters;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AMWD.Common.EntityFrameworkCore.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for the <see cref="ModelConfigurationBuilder"/> of entity framework core.
|
||||
/// </summary>
|
||||
public static class ModelConfigurationBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds converters for the <see cref="DateOnly"/> and <see cref="TimeOnly"/> datatypes introduced with .NET 6.0.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// As of 2022-06-04 only required for Microsoft SQL server on .NET 6.0.
|
||||
/// </remarks>
|
||||
/// <param name="builder">The <see cref="ModelConfigurationBuilder"/> instance.</param>
|
||||
/// <returns>The <see cref="ModelConfigurationBuilder"/> instance after applying the converters.</returns>
|
||||
public static ModelConfigurationBuilder AddDateOnlyTimeOnlyConverters(this ModelConfigurationBuilder builder)
|
||||
{
|
||||
builder.Properties<DateOnly>()
|
||||
.HaveConversion<DateOnlyConverter>()
|
||||
.HaveColumnType("date");
|
||||
builder.Properties<DateOnly?>()
|
||||
.HaveConversion<NullableDateOnlyConverter>()
|
||||
.HaveColumnType("date");
|
||||
|
||||
builder.Properties<TimeOnly>()
|
||||
.HaveConversion<TimeOnlyConverter>()
|
||||
.HaveColumnType("time");
|
||||
builder.Properties<TimeOnly?>()
|
||||
.HaveConversion<NullableTimeOnlyConverter>()
|
||||
.HaveColumnType("time");
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -36,8 +36,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.2">
|
||||
<PackageReference Include="Moq" Version="4.15.1" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
175
AMWD.Common.Moq/TcpClientMoq.cs
Normal file
175
AMWD.Common.Moq/TcpClientMoq.cs
Normal file
@@ -0,0 +1,175 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Moq;
|
||||
|
||||
namespace AMWD.Common.Moq
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapps the <see cref="Mock{TcpClient}"/> including the setup.
|
||||
/// </summary>
|
||||
public class TcpClientMoq
|
||||
{
|
||||
private readonly Mock<NetworkStream> streamMock;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TcpClientMoq"/> class.
|
||||
/// </summary>
|
||||
public TcpClientMoq()
|
||||
{
|
||||
Callbacks = new();
|
||||
Response = new byte[0];
|
||||
|
||||
streamMock = new();
|
||||
streamMock
|
||||
.Setup(s => s.WriteAsync(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
||||
.Callback<byte[], int, int, CancellationToken>((buffer, offset, count, _) =>
|
||||
{
|
||||
var callback = new TcpClientCallback
|
||||
{
|
||||
Buffer = new byte[count],
|
||||
Offset = offset,
|
||||
Count = count,
|
||||
Type = TcpClientCallback.WriteType.Asynchronous
|
||||
};
|
||||
Array.Copy(buffer, offset, callback.Buffer, 0, count);
|
||||
|
||||
Callbacks.Add(callback);
|
||||
})
|
||||
.Returns(Task.CompletedTask);
|
||||
streamMock
|
||||
.Setup(s => s.Write(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>()))
|
||||
.Callback<byte[], int, int>((buffer, offset, count) =>
|
||||
{
|
||||
var callback = new TcpClientCallback
|
||||
{
|
||||
Buffer = new byte[count],
|
||||
Offset = offset,
|
||||
Count = count,
|
||||
Type = TcpClientCallback.WriteType.Synchronous
|
||||
};
|
||||
Array.Copy(buffer, offset, callback.Buffer, 0, count);
|
||||
|
||||
Callbacks.Add(callback);
|
||||
});
|
||||
|
||||
streamMock
|
||||
.Setup(s => s.ReadAsync(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
||||
.Callback<byte[], int, int, CancellationToken>((buffer, offset, count, _) =>
|
||||
{
|
||||
byte[] bytes = Response ?? new byte[0];
|
||||
Array.Copy(bytes, 0, buffer, offset, Math.Min(bytes.Length, count));
|
||||
})
|
||||
.ReturnsAsync(Response?.Length ?? 0);
|
||||
streamMock
|
||||
.Setup(s => s.Read(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>()))
|
||||
.Callback<byte[], int, int>((buffer, offset, count) =>
|
||||
{
|
||||
byte[] bytes = Response ?? new byte[0];
|
||||
Array.Copy(bytes, 0, buffer, offset, Math.Min(bytes.Length, count));
|
||||
})
|
||||
.Returns(Response?.Length ?? 0);
|
||||
|
||||
Mock = new();
|
||||
Mock
|
||||
.Setup(c => c.GetStream())
|
||||
.Returns(streamMock.Object);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mocked <see cref="TcpClient"/>.
|
||||
/// </summary>
|
||||
public Mock<TcpClient> Mock { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the placed request.
|
||||
/// </summary>
|
||||
public List<TcpClientCallback> Callbacks { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the byte response, that should be "sent".
|
||||
/// </summary>
|
||||
public byte[] Response { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Resets the <see cref="Response"/> and <see cref="Callbacks"/>.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
Response = new byte[0];
|
||||
Callbacks.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the number of calls writing asynchronous to the stream.
|
||||
/// </summary>
|
||||
/// <param name="times">Number of calls.</param>
|
||||
public void VerifyWriteAsync(Times times)
|
||||
=> streamMock.Verify(s => s.WriteAsync(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<CancellationToken>()), times);
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the number of calls writing synchronous to the stream.
|
||||
/// </summary>
|
||||
/// <param name="times">Number of calls.</param>
|
||||
public void VerifyWriteSync(Times times)
|
||||
=> streamMock.Verify(s => s.Write(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>()), times);
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the number of calls reading asynchronous from the stream.
|
||||
/// </summary>
|
||||
/// <param name="times">Number of calls.</param>
|
||||
public void VerifyReadAsync(Times times)
|
||||
=> streamMock.Verify(s => s.ReadAsync(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<CancellationToken>()), times);
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the number of calls reading synchronous from the stream.
|
||||
/// </summary>
|
||||
/// <param name="times">Number of calls.</param>
|
||||
public void VerifyReadSync(Times times)
|
||||
=> streamMock.Verify(s => s.Read(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>()), times);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the placed TCP request.
|
||||
/// </summary>
|
||||
public class TcpClientCallback
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the type (a/synchronous call).
|
||||
/// </summary>
|
||||
public WriteType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the buffer content.
|
||||
/// </summary>
|
||||
public byte[] Buffer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the offset.
|
||||
/// </summary>
|
||||
public int Offset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the byte count.
|
||||
/// </summary>
|
||||
public int Count { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Lists the possible request types.
|
||||
/// </summary>
|
||||
public enum WriteType
|
||||
{
|
||||
/// <summary>
|
||||
/// The request was synchronous.
|
||||
/// </summary>
|
||||
Synchronous = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The request was asynchronous.
|
||||
/// </summary>
|
||||
Asynchronous = 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,13 +7,13 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.msbuild" Version="3.1.0">
|
||||
<PackageReference Include="coverlet.msbuild" Version="3.1.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.2.7" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.2.7" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
|
||||
<PackageReference Include="ReflectionMagic" Version="4.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -38,8 +38,8 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Dns" Version="7.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.1" />
|
||||
<PackageReference Include="Unclassified.DeepConvert" Version="1.3.0" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.2">
|
||||
<PackageReference Include="Unclassified.DeepConvert" Version="1.4.0" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -4,9 +4,16 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased](https://git.am-wd.de/AM.WD/common/compare/v1.4.3...master) - 0000-00-00
|
||||
## [Unreleased](https://git.am-wd.de/AM.WD/common/compare/v1.5.0...master) - 0000-00-00
|
||||
_nothing changed yet_
|
||||
|
||||
## [v1.5.0](https://git.am-wd.de/AM.WD/common/compare/v1.4.3...v1.5.0) - 2022-06-15
|
||||
### Added
|
||||
- `TcpClientMoq` to test communication via a `TcpClient`
|
||||
- EntityFramework Core Converters for new `DateOnly` and `TimeOnly` datatypes when using SQL Server on .NET 6.0 (Bug on Microsoft's EntityFramework)
|
||||
- `HttpContext.IsLocalRequest()` to determine whether the request was from local or remote.
|
||||
- `IPWhitelistAttribute` and `IPBlacklistAttribute` to allow/restrict access on specific controllers/actions.
|
||||
|
||||
|
||||
## [v1.4.3](https://git.am-wd.de/AM.WD/common/compare/v1.4.2...v1.4.3) - 2022-05-12
|
||||
### Fixed
|
||||
|
||||
Reference in New Issue
Block a user