Add RequestLogExpirationDuration and MaxRequestLogCount (#43) (#45)

This commit is contained in:
Stef Heyenrath
2017-08-20 11:20:35 +02:00
committed by GitHub
parent 76f0e04874
commit f8ad2cd3d6
19 changed files with 682 additions and 509 deletions

22
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,22 @@
{
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (WireMock.Net.StandAlone.NETCoreApp)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build_WireMock.Net.StandAlone.NETCoreApp",
"program": "${workspaceRoot}/examples/WireMock.Net.StandAlone.NETCoreApp/bin/Debug/netcoreapp2.0/WireMock.Net.StandAlone.NETCoreApp.dll",
"args": [],
"cwd": "${workspaceRoot}",
"stopAtEntry": false,
"console": "internalConsole"
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}"
}
]
}

17
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,17 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"taskName": "build_WireMock.Net.StandAlone.NETCoreApp",
"command": "dotnet build ${workspaceRoot}/examples/WireMock.Net.StandAlone.NETCoreApp/WireMock.Net.StandAlone.NETCoreApp.csproj -f netcoreapp2.0 ",
"type": "shell",
"group": "build",
"presentation": {
"reveal": "silent"
},
"problemMatcher": "$msCompile"
}
]
}

View File

@@ -12,7 +12,7 @@ namespace WireMock.Net.Client
static void Main(string[] args) static void Main(string[] args)
{ {
// Create an implementation of the IFluentMockServerAdmin and pass in the base URL for the API. // Create an implementation of the IFluentMockServerAdmin and pass in the base URL for the API.
var api = RestClient.For<IFluentMockServerAdmin>("http://localhost:9090"); var api = RestClient.For<IFluentMockServerAdmin>("http://localhost:9091");
// Set BASIC Auth // Set BASIC Auth
var value = Convert.ToBase64String(Encoding.ASCII.GetBytes("a:b")); var value = Convert.ToBase64String(Encoding.ASCII.GetBytes("a:b"));

View File

@@ -12,8 +12,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" /> <ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
<!--<ProjectReference Include="..\WireMock.Net\WireMock.Net.csproj" />--> <PackageReference Include="Newtonsoft.Json" Version="10.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -13,8 +13,8 @@ namespace WireMock.Net.ConsoleApplication
{ {
public static void Run() public static void Run()
{ {
string url1 = "http://localhost:9090/"; string url1 = "http://localhost:9091/";
string url2 = "http://localhost:9091/"; string url2 = "http://localhost:9092/";
string url3 = "https://localhost:9443/"; string url3 = "https://localhost:9443/";
var server = FluentMockServer.Start(new FluentMockServerSettings var server = FluentMockServer.Start(new FluentMockServerSettings

View File

@@ -1,15 +1,42 @@
using System; using System;
using System.Threading;
using WireMock.Server;
namespace WireMock.Net.StandAlone.NETCoreApp namespace WireMock.Net.StandAlone.NETCoreApp
{ {
class Program class Program
{ {
private static int sleepTime = 30000;
private static FluentMockServer server;
static void Main(string[] args) static void Main(string[] args)
{ {
StandAloneApp.Start(args); server = StandAloneApp.Start(args);
Console.WriteLine("Press any key to stop the server"); Console.WriteLine($"{DateTime.UtcNow} Press Ctrl+C to shut down");
Console.ReadKey();
System.Console.CancelKeyPress += (s,e) =>
{
Stop("CancelKeyPress");
};
System.Runtime.Loader.AssemblyLoadContext.Default.Unloading += ctx =>
{
Stop("AssemblyLoadContext.Default.Unloading");
};
while(true)
{
Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server running");
Thread.Sleep(sleepTime);
}
}
private static void Stop(string why)
{
Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server stopping because '{why}'");
server.Stop();
Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server stopped");
} }
} }
} }

View File

@@ -2,7 +2,7 @@
"profiles": { "profiles": {
"WireMock.Net.StandAlone.NETCoreApp": { "WireMock.Net.StandAlone.NETCoreApp": {
"commandName": "Project", "commandName": "Project",
"commandLineArgs": "--Urls http://*:9090" "commandLineArgs": "--Urls http://*:9091"
} }
} }
} }

View File

@@ -2,8 +2,8 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.0</TargetFramework> <TargetFrameworks>netcoreapp1.0;netcoreapp2.0</TargetFrameworks>
<RuntimeFrameworkVersion>1.0.1</RuntimeFrameworkVersion> <!-- <RuntimeFrameworkVersion>1.0.1</RuntimeFrameworkVersion> -->
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon> <ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>

View File

@@ -43,6 +43,12 @@ namespace WireMock.Net.StandAlone
[ValueArgument(typeof(string), "AdminPassword", Description = "The password needed for __admin access.", Optional = true)] [ValueArgument(typeof(string), "AdminPassword", Description = "The password needed for __admin access.", Optional = true)]
public string AdminPassword { get; set; } public string AdminPassword { get; set; }
[ValueArgument(typeof(int?), "RequestLogExpirationDuration", Description = "The RequestLog expiration in hours (optional).", Optional = true)]
public int? RequestLogExpirationDuration { get; set; }
[ValueArgument(typeof(int?), "MaxRequestLogCount", Description = "The MaxRequestLog count (optional).", Optional = true)]
public int? MaxRequestLogCount { get; set; }
} }
/// <summary> /// <summary>
@@ -76,7 +82,7 @@ namespace WireMock.Net.StandAlone
if (!options.Urls.Any()) if (!options.Urls.Any())
{ {
options.Urls.Add("http://localhost:9090/"); options.Urls.Add("http://localhost:9091/");
} }
var settings = new FluentMockServerSettings var settings = new FluentMockServerSettings
@@ -86,7 +92,9 @@ namespace WireMock.Net.StandAlone
ReadStaticMappings = options.ReadStaticMappings, ReadStaticMappings = options.ReadStaticMappings,
AllowPartialMapping = options.AllowPartialMapping, AllowPartialMapping = options.AllowPartialMapping,
AdminUsername = options.AdminUsername, AdminUsername = options.AdminUsername,
AdminPassword = options.AdminPassword AdminPassword = options.AdminPassword,
RequestLogExpirationDuration = options.RequestLogExpirationDuration,
MaxRequestLogCount = options.MaxRequestLogCount
}; };
if (!string.IsNullOrEmpty(options.ProxyURL)) if (!string.IsNullOrEmpty(options.ProxyURL))

View File

@@ -18,7 +18,7 @@
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/WireMock-Net/WireMock.Net</RepositoryUrl> <RepositoryUrl>https://github.com/WireMock-Net/WireMock.Net</RepositoryUrl>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild> <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<DebugType>full</DebugType> <DebugType>portable</DebugType>
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon> <ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
<RootNamespace>WireMock.Net.StandAlone</RootNamespace> <RootNamespace>WireMock.Net.StandAlone</RootNamespace>
</PropertyGroup> </PropertyGroup>

View File

@@ -14,5 +14,15 @@
/// Gets or sets if partial mapping is allowed. /// Gets or sets if partial mapping is allowed.
/// </summary> /// </summary>
public bool? AllowPartialMapping { get; set; } public bool? AllowPartialMapping { get; set; }
/// <summary>
/// Gets or sets the RequestLog expiration in hours
/// </summary>
public int? RequestLogExpirationDuration { get; set; }
/// <summary>
/// Gets or sets the MaxRequestLog count.
/// </summary>
public int? MaxRequestLogCount { get; set; }
} }
} }

View File

@@ -44,6 +44,7 @@ namespace WireMock.Owin
{ {
var request = await _requestMapper.MapAsync(ctx.Request); var request = await _requestMapper.MapAsync(ctx.Request);
bool logRequest = false;
ResponseMessage response = null; ResponseMessage response = null;
Mapping targetMapping = null; Mapping targetMapping = null;
RequestMatchResult requestMatchResult = null; RequestMatchResult requestMatchResult = null;
@@ -82,10 +83,13 @@ namespace WireMock.Owin
if (targetMapping == null) if (targetMapping == null)
{ {
logRequest = true;
response = new ResponseMessage { StatusCode = 404, Body = "No matching mapping found" }; response = new ResponseMessage { StatusCode = 404, Body = "No matching mapping found" };
return; return;
} }
logRequest = !targetMapping.IsAdminInterface;
if (targetMapping.IsAdminInterface && _options.AuthorizationMatcher != null) if (targetMapping.IsAdminInterface && _options.AuthorizationMatcher != null)
{ {
string authorization; string authorization;
@@ -120,7 +124,7 @@ namespace WireMock.Owin
RequestMatchResult = requestMatchResult RequestMatchResult = requestMatchResult
}; };
LogRequest(log); LogRequest(log, logRequest);
await _responseMapper.MapAsync(response, ctx.Response); await _responseMapper.MapAsync(response, ctx.Response);
} }
@@ -128,15 +132,25 @@ namespace WireMock.Owin
await CompletedTask; await CompletedTask;
} }
/// <summary> private void LogRequest(LogEntry entry, bool addRequest)
/// The log request.
/// </summary>
/// <param name="entry">The request.</param>
private void LogRequest(LogEntry entry)
{ {
lock (((ICollection)_options.LogEntries).SyncRoot) lock (((ICollection)_options.LogEntries).SyncRoot)
{ {
_options.LogEntries.Add(entry); if (addRequest)
{
_options.LogEntries.Add(entry);
}
if (_options.MaxRequestLogCount != null)
{
_options.LogEntries = _options.LogEntries.Skip(_options.LogEntries.Count - _options.MaxRequestLogCount.Value).ToList();
}
if (_options.RequestLogExpirationDuration != null)
{
var checkTime = DateTime.Now.AddHours(-_options.RequestLogExpirationDuration.Value);
_options.LogEntries = _options.LogEntries.Where(le => le.RequestMessage.DateTime > checkTime).ToList();
}
} }
} }
} }

View File

@@ -17,6 +17,10 @@ namespace WireMock.Owin
public IList<LogEntry> LogEntries { get; set; } public IList<LogEntry> LogEntries { get; set; }
public int? RequestLogExpirationDuration { get; set; }
public int? MaxRequestLogCount { get; set; }
public WireMockMiddlewareOptions() public WireMockMiddlewareOptions()
{ {
Mappings = new List<Mapping>(); Mappings = new List<Mapping>();

View File

@@ -161,6 +161,8 @@ namespace WireMock.Server
var model = new SettingsModel var model = new SettingsModel
{ {
AllowPartialMapping = _options.AllowPartialMapping, AllowPartialMapping = _options.AllowPartialMapping,
MaxRequestLogCount = _options.MaxRequestLogCount,
RequestLogExpirationDuration = _options.RequestLogExpirationDuration,
GlobalProcessingDelay = (int?)_options.RequestProcessingDelay?.TotalMilliseconds GlobalProcessingDelay = (int?)_options.RequestProcessingDelay?.TotalMilliseconds
}; };
@@ -174,6 +176,10 @@ namespace WireMock.Server
if (settings.AllowPartialMapping != null) if (settings.AllowPartialMapping != null)
_options.AllowPartialMapping = settings.AllowPartialMapping.Value; _options.AllowPartialMapping = settings.AllowPartialMapping.Value;
_options.MaxRequestLogCount = settings.MaxRequestLogCount;
_options.RequestLogExpirationDuration = settings.RequestLogExpirationDuration;
if (settings.GlobalProcessingDelay != null) if (settings.GlobalProcessingDelay != null)
_options.RequestProcessingDelay = TimeSpan.FromMilliseconds(settings.GlobalProcessingDelay.Value); _options.RequestProcessingDelay = TimeSpan.FromMilliseconds(settings.GlobalProcessingDelay.Value);

View File

@@ -197,6 +197,11 @@ namespace WireMock.Server
{ {
InitProxyAndRecord(settings.ProxyAndRecordSettings); InitProxyAndRecord(settings.ProxyAndRecordSettings);
} }
if (settings.MaxRequestLogCount != null)
{
SetMaxRequestLogCount(settings.MaxRequestLogCount);
}
} }
/// <summary> /// <summary>
@@ -316,6 +321,35 @@ namespace WireMock.Server
_options.AuthorizationMatcher = new RegexMatcher("^(?i)BASIC " + authorization + "$"); _options.AuthorizationMatcher = new RegexMatcher("^(?i)BASIC " + authorization + "$");
} }
/// <summary>
/// Removes the basic authentication.
/// </summary>
[PublicAPI]
public void RemoveBasicAuthentication()
{
_options.AuthorizationMatcher = null;
}
/// <summary>
/// Sets the maximum RequestLog count.
/// </summary>
/// <param name="maxRequestLogCount">The maximum RequestLog count.</param>
[PublicAPI]
public void SetMaxRequestLogCount([CanBeNull] int? maxRequestLogCount)
{
_options.MaxRequestLogCount = maxRequestLogCount;
}
/// <summary>
/// Sets RequestLog expiration in hours.
/// </summary>
/// <param name="requestLogExpirationDuration">The RequestLog expiration in hours.</param>
[PublicAPI]
public void SetRequestLogExpirationDuration([CanBeNull] int? requestLogExpirationDuration)
{
_options.RequestLogExpirationDuration = requestLogExpirationDuration;
}
/// <summary> /// <summary>
/// The given. /// The given.
/// </summary> /// </summary>

View File

@@ -69,5 +69,15 @@
/// The password needed for __admin access. /// The password needed for __admin access.
/// </summary> /// </summary>
public string AdminPassword { get; set; } public string AdminPassword { get; set; }
/// <summary>
/// The RequestLog expiration in hours (optional).
/// </summary>
public int? RequestLogExpirationDuration { get; set; }
/// <summary>
/// The MaxRequestLog count (optional).
/// </summary>
public int? MaxRequestLogCount { get; set; }
} }
} }

View File

@@ -18,7 +18,7 @@
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/WireMock-Net/WireMock.Net</RepositoryUrl> <RepositoryUrl>https://github.com/WireMock-Net/WireMock.Net</RepositoryUrl>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild> <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<DebugType>full</DebugType> <DebugType>portable</DebugType>
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon> <ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
<RootNamespace>WireMock</RootNamespace> <RootNamespace>WireMock</RootNamespace>
</PropertyGroup> </PropertyGroup>

View File

@@ -33,10 +33,10 @@ namespace WireMock.Net.Tests
[Fact] [Fact]
public void FluentMockServer_StartStop() public void FluentMockServer_StartStop()
{ {
var server1 = FluentMockServer.Start("http://localhost:9090/"); var server1 = FluentMockServer.Start("http://localhost:9091/");
server1.Stop(); server1.Stop();
var server2 = FluentMockServer.Start("http://localhost:9090/"); var server2 = FluentMockServer.Start("http://localhost:9091/");
server2.Stop(); server2.Stop();
} }
@@ -370,7 +370,7 @@ namespace WireMock.Net.Tests
} }
//Leaving commented as this requires an actual certificate with password, along with a service that expects a client certificate //Leaving commented as this requires an actual certificate with password, along with a service that expects a client certificate
[Fact] //[Fact]
//public async Task Should_proxy_responses_with_client_certificate() //public async Task Should_proxy_responses_with_client_certificate()
//{ //{
// // given // // given
@@ -386,7 +386,29 @@ namespace WireMock.Net.Tests
// Check.That(result).Contains("google"); // Check.That(result).Contains("google");
//} //}
//[TearDown] [Fact]
public async Task FluentMockServer_Logging_SetMaxRequestLogCount()
{
// Assign
var client = new HttpClient();
// Act
_server = FluentMockServer.Start();
_server.SetMaxRequestLogCount(2);
await client.GetAsync("http://localhost:" + _server.Ports[0] + "/foo1");
await client.GetAsync("http://localhost:" + _server.Ports[0] + "/foo2");
await client.GetAsync("http://localhost:" + _server.Ports[0] + "/foo3");
// Assert
Check.That(_server.LogEntries).HasSize(2);
var requestLoggedA = _server.LogEntries.First();
Check.That(requestLoggedA.RequestMessage.Path).EndsWith("/foo2");
var requestLoggedB = _server.LogEntries.Last();
Check.That(requestLoggedB.RequestMessage.Path).EndsWith("/foo3");
}
public void Dispose() public void Dispose()
{ {
_server?.Stop(); _server?.Stop();

View File

@@ -18,7 +18,7 @@
}, },
"Response": { "Response": {
"StatusCode": 200, "StatusCode": 200,
"Body": "{\"_self\":\"\",\"id\":\"abc\",\"_rid\":\"abc.documents.azure.com\",\"media\":\"//media/\",\"addresses\":\"//addresses/\",\"_dbs\":\"//dbs/\",\"writableLocations\":[{\"name\":\"West Europe\",\"databaseAccountEndpoint\":\"http://localhost:9090/\"}],\"readableLocations\":[{\"name\":\"West Europe\",\"databaseAccountEndpoint\":\"http://localhost:9090/\"}],\"userReplicationPolicy\":{\"asyncReplication\":false,\"minReplicaSetSize\":3,\"maxReplicasetSize\":4},\"userConsistencyPolicy\":{\"defaultConsistencyLevel\":\"Session\"},\"systemReplicationPolicy\":{\"minReplicaSetSize\":3,\"maxReplicasetSize\":4},\"readPolicy\":{\"primaryReadCoefficient\":1,\"secondaryReadCoefficient\":1},\"queryEngineConfiguration\":\"{\\\"maxSqlQueryInputLength\\\":30720,\\\"maxJoinsPerSqlQuery\\\":5,\\\"maxLogicalAndPerSqlQuery\\\":500,\\\"maxLogicalOrPerSqlQuery\\\":500,\\\"maxUdfRefPerSqlQuery\\\":2,\\\"maxInExpressionItemsCount\\\":8000,\\\"queryMaxInMemorySortDocumentCount\\\":500,\\\"maxQueryRequestTimeoutFraction\\\":0.9,\\\"sqlAllowNonFiniteNumbers\\\":false,\\\"sqlAllowAggregateFunctions\\\":true,\\\"sqlAllowSubQuery\\\":false,\\\"allowNewKeywords\\\":true,\\\"sqlAllowLike\\\":false,\\\"maxSpatialQueryCells\\\":12,\\\"spatialMaxGeometryPointCount\\\":256,\\\"sqlAllowTop\\\":true,\\\"enableSpatialIndexing\\\":true}\"}", "Body": "{\"_self\":\"\",\"id\":\"abc\",\"_rid\":\"abc.documents.azure.com\",\"media\":\"//media/\",\"addresses\":\"//addresses/\",\"_dbs\":\"//dbs/\",\"writableLocations\":[{\"name\":\"West Europe\",\"databaseAccountEndpoint\":\"http://localhost:9091/\"}],\"readableLocations\":[{\"name\":\"West Europe\",\"databaseAccountEndpoint\":\"http://localhost:9091/\"}],\"userReplicationPolicy\":{\"asyncReplication\":false,\"minReplicaSetSize\":3,\"maxReplicasetSize\":4},\"userConsistencyPolicy\":{\"defaultConsistencyLevel\":\"Session\"},\"systemReplicationPolicy\":{\"minReplicaSetSize\":3,\"maxReplicasetSize\":4},\"readPolicy\":{\"primaryReadCoefficient\":1,\"secondaryReadCoefficient\":1},\"queryEngineConfiguration\":\"{\\\"maxSqlQueryInputLength\\\":30720,\\\"maxJoinsPerSqlQuery\\\":5,\\\"maxLogicalAndPerSqlQuery\\\":500,\\\"maxLogicalOrPerSqlQuery\\\":500,\\\"maxUdfRefPerSqlQuery\\\":2,\\\"maxInExpressionItemsCount\\\":8000,\\\"queryMaxInMemorySortDocumentCount\\\":500,\\\"maxQueryRequestTimeoutFraction\\\":0.9,\\\"sqlAllowNonFiniteNumbers\\\":false,\\\"sqlAllowAggregateFunctions\\\":true,\\\"sqlAllowSubQuery\\\":false,\\\"allowNewKeywords\\\":true,\\\"sqlAllowLike\\\":false,\\\"maxSpatialQueryCells\\\":12,\\\"spatialMaxGeometryPointCount\\\":256,\\\"sqlAllowTop\\\":true,\\\"enableSpatialIndexing\\\":true}\"}",
"BodyEncoding": { "BodyEncoding": {
"CodePage": 65001, "CodePage": 65001,
"EncodingName": "Unicode (UTF-8)", "EncodingName": "Unicode (UTF-8)",
@@ -33,7 +33,7 @@
"x-ms-gatewayversion": "version=1.11.164.3", "x-ms-gatewayversion": "version=1.11.164.3",
"x-ms-media-storage-usage-mb": "0", "x-ms-media-storage-usage-mb": "0",
"x-ms-databaseaccount-provisioned-mb": "0", "x-ms-databaseaccount-provisioned-mb": "0",
"Content-Location": "http://localhost:9090/", "Content-Location": "http://localhost:9091/",
"Date": "Mon, 06 Mar 2017 10:56:40 GMT", "Date": "Mon, 06 Mar 2017 10:56:40 GMT",
"Content-Type": "application/json", "Content-Type": "application/json",
"Server": "Microsoft-HTTPAPI/2.0" "Server": "Microsoft-HTTPAPI/2.0"