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

View File

@@ -43,6 +43,12 @@ namespace WireMock.Net.StandAlone
[ValueArgument(typeof(string), "AdminPassword", Description = "The password needed for __admin access.", Optional = true)]
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>
@@ -76,7 +82,7 @@ namespace WireMock.Net.StandAlone
if (!options.Urls.Any())
{
options.Urls.Add("http://localhost:9090/");
options.Urls.Add("http://localhost:9091/");
}
var settings = new FluentMockServerSettings
@@ -86,7 +92,9 @@ namespace WireMock.Net.StandAlone
ReadStaticMappings = options.ReadStaticMappings,
AllowPartialMapping = options.AllowPartialMapping,
AdminUsername = options.AdminUsername,
AdminPassword = options.AdminPassword
AdminPassword = options.AdminPassword,
RequestLogExpirationDuration = options.RequestLogExpirationDuration,
MaxRequestLogCount = options.MaxRequestLogCount
};
if (!string.IsNullOrEmpty(options.ProxyURL))

View File

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

View File

@@ -14,5 +14,15 @@
/// Gets or sets if partial mapping is allowed.
/// </summary>
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

@@ -1,143 +1,157 @@
using System;
using System.Collections;
using System.Threading.Tasks;
using WireMock.Logging;
using WireMock.Matchers.Request;
using System.Linq;
#if !NETSTANDARD
using Microsoft.Owin;
#else
using Microsoft.AspNetCore.Http;
#endif
namespace WireMock.Owin
{
#if !NETSTANDARD
internal class WireMockMiddleware : OwinMiddleware
#else
internal class WireMockMiddleware
#endif
{
private static readonly Task CompletedTask = Task.FromResult(false);
private readonly WireMockMiddlewareOptions _options;
private readonly OwinRequestMapper _requestMapper = new OwinRequestMapper();
private readonly OwinResponseMapper _responseMapper = new OwinResponseMapper();
#if !NETSTANDARD
public WireMockMiddleware(OwinMiddleware next, WireMockMiddlewareOptions options) : base(next)
{
_options = options;
}
#else
public WireMockMiddleware(RequestDelegate next, WireMockMiddlewareOptions options)
{
_options = options;
}
#endif
#if !NETSTANDARD
public override async Task Invoke(IOwinContext ctx)
#else
public async Task Invoke(HttpContext ctx)
#endif
{
var request = await _requestMapper.MapAsync(ctx.Request);
ResponseMessage response = null;
Mapping targetMapping = null;
RequestMatchResult requestMatchResult = null;
try
{
var mappings = _options.Mappings
.Select(m => new
{
Mapping = m,
MatchResult = m.IsRequestHandled(request)
})
.ToList();
if (_options.AllowPartialMapping)
{
var partialMappings = mappings
.Where(pm => pm.Mapping.IsAdminInterface && pm.MatchResult.IsPerfectMatch || !pm.Mapping.IsAdminInterface)
.OrderBy(m => m.MatchResult)
.ThenBy(m => m.Mapping.Priority)
.ToList();
var bestPartialMatch = partialMappings.FirstOrDefault(pm => pm.MatchResult.AverageTotalScore > 0.0);
targetMapping = bestPartialMatch?.Mapping;
requestMatchResult = bestPartialMatch?.MatchResult;
}
else
{
var perfectMatch = mappings
.OrderBy(m => m.Mapping.Priority)
.FirstOrDefault(m => m.MatchResult.IsPerfectMatch);
targetMapping = perfectMatch?.Mapping;
requestMatchResult = perfectMatch?.MatchResult;
}
if (targetMapping == null)
{
response = new ResponseMessage { StatusCode = 404, Body = "No matching mapping found" };
return;
}
if (targetMapping.IsAdminInterface && _options.AuthorizationMatcher != null)
{
string authorization;
bool present = request.Headers.TryGetValue("Authorization", out authorization);
if (!present || _options.AuthorizationMatcher.IsMatch(authorization) < 1.0)
{
response = new ResponseMessage { StatusCode = 401 };
return;
}
}
if (!targetMapping.IsAdminInterface && _options.RequestProcessingDelay > TimeSpan.Zero)
{
await Task.Delay(_options.RequestProcessingDelay.Value);
}
response = await targetMapping.ResponseToAsync(request);
}
catch (Exception ex)
{
response = new ResponseMessage { StatusCode = 500, Body = ex.ToString() };
}
finally
{
var log = new LogEntry
{
Guid = Guid.NewGuid(),
RequestMessage = request,
ResponseMessage = response,
MappingGuid = targetMapping?.Guid,
MappingTitle = targetMapping?.Title,
RequestMatchResult = requestMatchResult
};
LogRequest(log);
await _responseMapper.MapAsync(response, ctx.Response);
}
await CompletedTask;
}
/// <summary>
/// The log request.
/// </summary>
/// <param name="entry">The request.</param>
private void LogRequest(LogEntry entry)
{
lock (((ICollection)_options.LogEntries).SyncRoot)
{
_options.LogEntries.Add(entry);
}
}
}
using System;
using System.Collections;
using System.Threading.Tasks;
using WireMock.Logging;
using WireMock.Matchers.Request;
using System.Linq;
#if !NETSTANDARD
using Microsoft.Owin;
#else
using Microsoft.AspNetCore.Http;
#endif
namespace WireMock.Owin
{
#if !NETSTANDARD
internal class WireMockMiddleware : OwinMiddleware
#else
internal class WireMockMiddleware
#endif
{
private static readonly Task CompletedTask = Task.FromResult(false);
private readonly WireMockMiddlewareOptions _options;
private readonly OwinRequestMapper _requestMapper = new OwinRequestMapper();
private readonly OwinResponseMapper _responseMapper = new OwinResponseMapper();
#if !NETSTANDARD
public WireMockMiddleware(OwinMiddleware next, WireMockMiddlewareOptions options) : base(next)
{
_options = options;
}
#else
public WireMockMiddleware(RequestDelegate next, WireMockMiddlewareOptions options)
{
_options = options;
}
#endif
#if !NETSTANDARD
public override async Task Invoke(IOwinContext ctx)
#else
public async Task Invoke(HttpContext ctx)
#endif
{
var request = await _requestMapper.MapAsync(ctx.Request);
bool logRequest = false;
ResponseMessage response = null;
Mapping targetMapping = null;
RequestMatchResult requestMatchResult = null;
try
{
var mappings = _options.Mappings
.Select(m => new
{
Mapping = m,
MatchResult = m.IsRequestHandled(request)
})
.ToList();
if (_options.AllowPartialMapping)
{
var partialMappings = mappings
.Where(pm => pm.Mapping.IsAdminInterface && pm.MatchResult.IsPerfectMatch || !pm.Mapping.IsAdminInterface)
.OrderBy(m => m.MatchResult)
.ThenBy(m => m.Mapping.Priority)
.ToList();
var bestPartialMatch = partialMappings.FirstOrDefault(pm => pm.MatchResult.AverageTotalScore > 0.0);
targetMapping = bestPartialMatch?.Mapping;
requestMatchResult = bestPartialMatch?.MatchResult;
}
else
{
var perfectMatch = mappings
.OrderBy(m => m.Mapping.Priority)
.FirstOrDefault(m => m.MatchResult.IsPerfectMatch);
targetMapping = perfectMatch?.Mapping;
requestMatchResult = perfectMatch?.MatchResult;
}
if (targetMapping == null)
{
logRequest = true;
response = new ResponseMessage { StatusCode = 404, Body = "No matching mapping found" };
return;
}
logRequest = !targetMapping.IsAdminInterface;
if (targetMapping.IsAdminInterface && _options.AuthorizationMatcher != null)
{
string authorization;
bool present = request.Headers.TryGetValue("Authorization", out authorization);
if (!present || _options.AuthorizationMatcher.IsMatch(authorization) < 1.0)
{
response = new ResponseMessage { StatusCode = 401 };
return;
}
}
if (!targetMapping.IsAdminInterface && _options.RequestProcessingDelay > TimeSpan.Zero)
{
await Task.Delay(_options.RequestProcessingDelay.Value);
}
response = await targetMapping.ResponseToAsync(request);
}
catch (Exception ex)
{
response = new ResponseMessage { StatusCode = 500, Body = ex.ToString() };
}
finally
{
var log = new LogEntry
{
Guid = Guid.NewGuid(),
RequestMessage = request,
ResponseMessage = response,
MappingGuid = targetMapping?.Guid,
MappingTitle = targetMapping?.Title,
RequestMatchResult = requestMatchResult
};
LogRequest(log, logRequest);
await _responseMapper.MapAsync(response, ctx.Response);
}
await CompletedTask;
}
private void LogRequest(LogEntry entry, bool addRequest)
{
lock (((ICollection)_options.LogEntries).SyncRoot)
{
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

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

View File

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

View File

@@ -1,347 +1,381 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using JetBrains.Annotations;
using WireMock.Http;
using WireMock.Matchers;
using WireMock.Matchers.Request;
using WireMock.RequestBuilders;
using WireMock.Settings;
using WireMock.Validation;
using WireMock.Owin;
namespace WireMock.Server
{
/// <summary>
/// The fluent mock server.
/// </summary>
public partial class FluentMockServer : IDisposable
{
private readonly IOwinSelfHost _httpServer;
private readonly object _syncRoot = new object();
private readonly WireMockMiddlewareOptions _options = new WireMockMiddlewareOptions();
/// <summary>
/// Gets the ports.
/// </summary>
/// <value>
/// The ports.
/// </value>
[PublicAPI]
public List<int> Ports { get; }
/// <summary>
/// Gets the urls.
/// </summary>
[PublicAPI]
public string[] Urls { get; }
/// <summary>
/// Gets the mappings.
/// </summary>
[PublicAPI]
public IEnumerable<Mapping> Mappings
{
get
{
lock (((ICollection)_options.Mappings).SyncRoot)
{
return new ReadOnlyCollection<Mapping>(_options.Mappings);
}
}
}
#region Start/Stop
/// <summary>
/// Starts the specified settings.
/// </summary>
/// <param name="settings">The FluentMockServerSettings.</param>
/// <returns>The <see cref="FluentMockServer"/>.</returns>
[PublicAPI]
public static FluentMockServer Start(FluentMockServerSettings settings)
{
Check.NotNull(settings, nameof(settings));
return new FluentMockServer(settings);
}
/// <summary>
/// Start this FluentMockServer.
/// </summary>
/// <param name="port">The port.</param>
/// <param name="ssl">The SSL support.</param>
/// <returns>The <see cref="FluentMockServer"/>.</returns>
[PublicAPI]
public static FluentMockServer Start([CanBeNull] int? port = 0, bool ssl = false)
{
return new FluentMockServer(new FluentMockServerSettings
{
Port = port,
UseSSL = ssl
});
}
/// <summary>
/// Start this FluentMockServer.
/// </summary>
/// <param name="urls">The urls to listen on.</param>
/// <returns>The <see cref="FluentMockServer"/>.</returns>
[PublicAPI]
public static FluentMockServer Start(params string[] urls)
{
Check.NotEmpty(urls, nameof(urls));
return new FluentMockServer(new FluentMockServerSettings
{
Urls = urls
});
}
/// <summary>
/// Start this FluentMockServer with the admin interface.
/// </summary>
/// <param name="port">The port.</param>
/// <param name="ssl">The SSL support.</param>
/// <returns>The <see cref="FluentMockServer"/>.</returns>
[PublicAPI]
public static FluentMockServer StartWithAdminInterface(int? port = 0, bool ssl = false)
{
return new FluentMockServer(new FluentMockServerSettings
{
Port = port,
UseSSL = ssl,
StartAdminInterface = true
});
}
/// <summary>
/// Start this FluentMockServer with the admin interface.
/// </summary>
/// <param name="urls">The urls.</param>
/// <returns>The <see cref="FluentMockServer"/>.</returns>
[PublicAPI]
public static FluentMockServer StartWithAdminInterface(params string[] urls)
{
Check.NotEmpty(urls, nameof(urls));
return new FluentMockServer(new FluentMockServerSettings
{
Urls = urls,
StartAdminInterface = true
});
}
/// <summary>
/// Start this FluentMockServer with the admin interface and read static mappings.
/// </summary>
/// <param name="urls">The urls.</param>
/// <returns>The <see cref="FluentMockServer"/>.</returns>
[PublicAPI]
public static FluentMockServer StartWithAdminInterfaceAndReadStaticMappings(params string[] urls)
{
Check.NotEmpty(urls, nameof(urls));
return new FluentMockServer(new FluentMockServerSettings
{
Urls = urls,
StartAdminInterface = true,
ReadStaticMappings = true
});
}
private FluentMockServer(FluentMockServerSettings settings)
{
if (settings.Urls != null)
{
Urls = settings.Urls;
}
else
{
int port = settings.Port > 0 ? settings.Port.Value : PortUtil.FindFreeTcpPort();
Urls = new[] { (settings.UseSSL == true ? "https" : "http") + "://localhost:" + port + "/" };
}
#if NETSTANDARD
_httpServer = new AspNetCoreSelfHost(_options, Urls);
#else
_httpServer = new OwinSelfHost(_options, Urls);
#endif
Ports = _httpServer.Ports;
_httpServer.StartAsync();
if (settings.AllowPartialMapping == true)
{
AllowPartialMapping();
}
if (settings.StartAdminInterface == true)
{
if (!string.IsNullOrEmpty(settings.AdminUsername) && !string.IsNullOrEmpty(settings.AdminPassword))
{
SetBasicAuthentication(settings.AdminUsername, settings.AdminPassword);
}
InitAdmin();
}
if (settings.ReadStaticMappings == true)
{
ReadStaticMappings();
}
if (settings.ProxyAndRecordSettings != null)
{
InitProxyAndRecord(settings.ProxyAndRecordSettings);
}
}
/// <summary>
/// Stop this server.
/// </summary>
[PublicAPI]
public void Stop()
{
_httpServer?.StopAsync();
}
#endregion
/// <summary>
/// Adds the catch all mapping.
/// </summary>
[PublicAPI]
public void AddCatchAllMapping()
{
Given(Request.Create().WithPath("/*").UsingAnyVerb())
.WithGuid(Guid.Parse("90008000-0000-4444-a17e-669cd84f1f05"))
.AtPriority(1000)
.RespondWith(new DynamicResponseProvider(request => new ResponseMessage { StatusCode = 404, Body = "No matching mapping found" }));
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
if (_httpServer != null && _httpServer.IsStarted)
{
_httpServer.StopAsync();
}
}
/// <summary>
/// Resets LogEntries and Mappings.
/// </summary>
[PublicAPI]
public void Reset()
{
ResetLogEntries();
ResetMappings();
}
/// <summary>
/// Resets the Mappings.
/// </summary>
[PublicAPI]
public void ResetMappings()
{
lock (((ICollection)_options.Mappings).SyncRoot)
{
_options.Mappings = _options.Mappings.Where(m => m.IsAdminInterface).ToList();
}
}
/// <summary>
/// Deletes the mapping.
/// </summary>
/// <param name="guid">The unique identifier.</param>
[PublicAPI]
public bool DeleteMapping(Guid guid)
{
lock (((ICollection)_options.Mappings).SyncRoot)
{
// Check a mapping exists with the same GUID, if so, remove it.
var existingMapping = _options.Mappings.FirstOrDefault(m => m.Guid == guid);
if (existingMapping != null)
{
_options.Mappings.Remove(existingMapping);
return true;
}
return false;
}
}
/// <summary>
/// The add request processing delay.
/// </summary>
/// <param name="delay">The delay.</param>
[PublicAPI]
public void AddGlobalProcessingDelay(TimeSpan delay)
{
lock (_syncRoot)
{
_options.RequestProcessingDelay = delay;
}
}
/// <summary>
/// Allows the partial mapping.
/// </summary>
[PublicAPI]
public void AllowPartialMapping()
{
lock (_syncRoot)
{
_options.AllowPartialMapping = true;
}
}
/// <summary>
/// Sets the basic authentication.
/// </summary>
/// <param name="username">The username.</param>
/// <param name="password">The password.</param>
[PublicAPI]
public void SetBasicAuthentication([NotNull] string username, [NotNull] string password)
{
Check.NotNull(username, nameof(username));
Check.NotNull(password, nameof(password));
string authorization = Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password));
_options.AuthorizationMatcher = new RegexMatcher("^(?i)BASIC " + authorization + "$");
}
/// <summary>
/// The given.
/// </summary>
/// <param name="requestMatcher">The request matcher.</param>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
[PublicAPI]
public IRespondWithAProvider Given(IRequestMatcher requestMatcher)
{
return new RespondWithAProvider(RegisterMapping, requestMatcher);
}
/// <summary>
/// The register mapping.
/// </summary>
/// <param name="mapping">
/// The mapping.
/// </param>
private void RegisterMapping(Mapping mapping)
{
lock (((ICollection)_options.Mappings).SyncRoot)
{
// Check a mapping exists with the same GUID, if so, remove it first.
DeleteMapping(mapping.Guid);
_options.Mappings.Add(mapping);
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using JetBrains.Annotations;
using WireMock.Http;
using WireMock.Matchers;
using WireMock.Matchers.Request;
using WireMock.RequestBuilders;
using WireMock.Settings;
using WireMock.Validation;
using WireMock.Owin;
namespace WireMock.Server
{
/// <summary>
/// The fluent mock server.
/// </summary>
public partial class FluentMockServer : IDisposable
{
private readonly IOwinSelfHost _httpServer;
private readonly object _syncRoot = new object();
private readonly WireMockMiddlewareOptions _options = new WireMockMiddlewareOptions();
/// <summary>
/// Gets the ports.
/// </summary>
/// <value>
/// The ports.
/// </value>
[PublicAPI]
public List<int> Ports { get; }
/// <summary>
/// Gets the urls.
/// </summary>
[PublicAPI]
public string[] Urls { get; }
/// <summary>
/// Gets the mappings.
/// </summary>
[PublicAPI]
public IEnumerable<Mapping> Mappings
{
get
{
lock (((ICollection)_options.Mappings).SyncRoot)
{
return new ReadOnlyCollection<Mapping>(_options.Mappings);
}
}
}
#region Start/Stop
/// <summary>
/// Starts the specified settings.
/// </summary>
/// <param name="settings">The FluentMockServerSettings.</param>
/// <returns>The <see cref="FluentMockServer"/>.</returns>
[PublicAPI]
public static FluentMockServer Start(FluentMockServerSettings settings)
{
Check.NotNull(settings, nameof(settings));
return new FluentMockServer(settings);
}
/// <summary>
/// Start this FluentMockServer.
/// </summary>
/// <param name="port">The port.</param>
/// <param name="ssl">The SSL support.</param>
/// <returns>The <see cref="FluentMockServer"/>.</returns>
[PublicAPI]
public static FluentMockServer Start([CanBeNull] int? port = 0, bool ssl = false)
{
return new FluentMockServer(new FluentMockServerSettings
{
Port = port,
UseSSL = ssl
});
}
/// <summary>
/// Start this FluentMockServer.
/// </summary>
/// <param name="urls">The urls to listen on.</param>
/// <returns>The <see cref="FluentMockServer"/>.</returns>
[PublicAPI]
public static FluentMockServer Start(params string[] urls)
{
Check.NotEmpty(urls, nameof(urls));
return new FluentMockServer(new FluentMockServerSettings
{
Urls = urls
});
}
/// <summary>
/// Start this FluentMockServer with the admin interface.
/// </summary>
/// <param name="port">The port.</param>
/// <param name="ssl">The SSL support.</param>
/// <returns>The <see cref="FluentMockServer"/>.</returns>
[PublicAPI]
public static FluentMockServer StartWithAdminInterface(int? port = 0, bool ssl = false)
{
return new FluentMockServer(new FluentMockServerSettings
{
Port = port,
UseSSL = ssl,
StartAdminInterface = true
});
}
/// <summary>
/// Start this FluentMockServer with the admin interface.
/// </summary>
/// <param name="urls">The urls.</param>
/// <returns>The <see cref="FluentMockServer"/>.</returns>
[PublicAPI]
public static FluentMockServer StartWithAdminInterface(params string[] urls)
{
Check.NotEmpty(urls, nameof(urls));
return new FluentMockServer(new FluentMockServerSettings
{
Urls = urls,
StartAdminInterface = true
});
}
/// <summary>
/// Start this FluentMockServer with the admin interface and read static mappings.
/// </summary>
/// <param name="urls">The urls.</param>
/// <returns>The <see cref="FluentMockServer"/>.</returns>
[PublicAPI]
public static FluentMockServer StartWithAdminInterfaceAndReadStaticMappings(params string[] urls)
{
Check.NotEmpty(urls, nameof(urls));
return new FluentMockServer(new FluentMockServerSettings
{
Urls = urls,
StartAdminInterface = true,
ReadStaticMappings = true
});
}
private FluentMockServer(FluentMockServerSettings settings)
{
if (settings.Urls != null)
{
Urls = settings.Urls;
}
else
{
int port = settings.Port > 0 ? settings.Port.Value : PortUtil.FindFreeTcpPort();
Urls = new[] { (settings.UseSSL == true ? "https" : "http") + "://localhost:" + port + "/" };
}
#if NETSTANDARD
_httpServer = new AspNetCoreSelfHost(_options, Urls);
#else
_httpServer = new OwinSelfHost(_options, Urls);
#endif
Ports = _httpServer.Ports;
_httpServer.StartAsync();
if (settings.AllowPartialMapping == true)
{
AllowPartialMapping();
}
if (settings.StartAdminInterface == true)
{
if (!string.IsNullOrEmpty(settings.AdminUsername) && !string.IsNullOrEmpty(settings.AdminPassword))
{
SetBasicAuthentication(settings.AdminUsername, settings.AdminPassword);
}
InitAdmin();
}
if (settings.ReadStaticMappings == true)
{
ReadStaticMappings();
}
if (settings.ProxyAndRecordSettings != null)
{
InitProxyAndRecord(settings.ProxyAndRecordSettings);
}
if (settings.MaxRequestLogCount != null)
{
SetMaxRequestLogCount(settings.MaxRequestLogCount);
}
}
/// <summary>
/// Stop this server.
/// </summary>
[PublicAPI]
public void Stop()
{
_httpServer?.StopAsync();
}
#endregion
/// <summary>
/// Adds the catch all mapping.
/// </summary>
[PublicAPI]
public void AddCatchAllMapping()
{
Given(Request.Create().WithPath("/*").UsingAnyVerb())
.WithGuid(Guid.Parse("90008000-0000-4444-a17e-669cd84f1f05"))
.AtPriority(1000)
.RespondWith(new DynamicResponseProvider(request => new ResponseMessage { StatusCode = 404, Body = "No matching mapping found" }));
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
if (_httpServer != null && _httpServer.IsStarted)
{
_httpServer.StopAsync();
}
}
/// <summary>
/// Resets LogEntries and Mappings.
/// </summary>
[PublicAPI]
public void Reset()
{
ResetLogEntries();
ResetMappings();
}
/// <summary>
/// Resets the Mappings.
/// </summary>
[PublicAPI]
public void ResetMappings()
{
lock (((ICollection)_options.Mappings).SyncRoot)
{
_options.Mappings = _options.Mappings.Where(m => m.IsAdminInterface).ToList();
}
}
/// <summary>
/// Deletes the mapping.
/// </summary>
/// <param name="guid">The unique identifier.</param>
[PublicAPI]
public bool DeleteMapping(Guid guid)
{
lock (((ICollection)_options.Mappings).SyncRoot)
{
// Check a mapping exists with the same GUID, if so, remove it.
var existingMapping = _options.Mappings.FirstOrDefault(m => m.Guid == guid);
if (existingMapping != null)
{
_options.Mappings.Remove(existingMapping);
return true;
}
return false;
}
}
/// <summary>
/// The add request processing delay.
/// </summary>
/// <param name="delay">The delay.</param>
[PublicAPI]
public void AddGlobalProcessingDelay(TimeSpan delay)
{
lock (_syncRoot)
{
_options.RequestProcessingDelay = delay;
}
}
/// <summary>
/// Allows the partial mapping.
/// </summary>
[PublicAPI]
public void AllowPartialMapping()
{
lock (_syncRoot)
{
_options.AllowPartialMapping = true;
}
}
/// <summary>
/// Sets the basic authentication.
/// </summary>
/// <param name="username">The username.</param>
/// <param name="password">The password.</param>
[PublicAPI]
public void SetBasicAuthentication([NotNull] string username, [NotNull] string password)
{
Check.NotNull(username, nameof(username));
Check.NotNull(password, nameof(password));
string authorization = Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password));
_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>
/// The given.
/// </summary>
/// <param name="requestMatcher">The request matcher.</param>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
[PublicAPI]
public IRespondWithAProvider Given(IRequestMatcher requestMatcher)
{
return new RespondWithAProvider(RegisterMapping, requestMatcher);
}
/// <summary>
/// The register mapping.
/// </summary>
/// <param name="mapping">
/// The mapping.
/// </param>
private void RegisterMapping(Mapping mapping)
{
lock (((ICollection)_options.Mappings).SyncRoot)
{
// Check a mapping exists with the same GUID, if so, remove it first.
DeleteMapping(mapping.Guid);
_options.Mappings.Add(mapping);
}
}
}
}

View File

@@ -69,5 +69,15 @@
/// The password needed for __admin access.
/// </summary>
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>
<RepositoryUrl>https://github.com/WireMock-Net/WireMock.Net</RepositoryUrl>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<DebugType>full</DebugType>
<DebugType>portable</DebugType>
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
<RootNamespace>WireMock</RootNamespace>
</PropertyGroup>