using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using WireMock.Http;
using WireMock.Logging;
using WireMock.Matchers;
using WireMock.Matchers.Request;
using WireMock.RequestBuilders;
using WireMock.Validation;
using WireMock.Owin;
namespace WireMock.Server
{
///
/// The fluent mock server.
///
public partial class FluentMockServer : IDisposable
{
private readonly IOwinSelfHost _httpServer;
private readonly object _syncRoot = new object();
private readonly WireMockMiddlewareOptions _options = new WireMockMiddlewareOptions();
///
/// Gets the ports.
///
///
/// The ports.
///
[PublicAPI]
public List Ports { get; }
///
/// Gets the urls.
///
[PublicAPI]
public string[] Urls { get; }
///
/// Gets the request logs.
///
[PublicAPI]
public IEnumerable LogEntries
{
get
{
lock (((ICollection)_options.LogEntries).SyncRoot)
{
return new ReadOnlyCollection(_options.LogEntries);
}
}
}
///
/// The search log-entries based on matchers.
///
/// The matchers.
/// The .
[PublicAPI]
public IEnumerable FindLogEntries([NotNull] params IRequestMatcher[] matchers)
{
lock (((ICollection)_options.LogEntries).SyncRoot)
{
var results = new Dictionary();
foreach (var log in _options.LogEntries)
{
var requestMatchResult = new RequestMatchResult();
foreach (var matcher in matchers)
{
matcher.GetMatchingScore(log.RequestMessage, requestMatchResult);
}
if (requestMatchResult.AverageTotalScore > 0.99)
results.Add(log, requestMatchResult);
}
return new ReadOnlyCollection(results.OrderBy(x => x.Value).Select(x => x.Key).ToList());
}
}
///
/// Gets the mappings.
///
[PublicAPI]
public IEnumerable Mappings
{
get
{
lock (((ICollection)_options.Mappings).SyncRoot)
{
return new ReadOnlyCollection(_options.Mappings);
}
}
}
///
/// Starts the specified settings.
///
/// The FluentMockServerSettings.
/// The .
[PublicAPI]
public static FluentMockServer Start(FluentMockServerSettings settings)
{
Check.NotNull(settings, nameof(settings));
return new FluentMockServer(settings);
}
///
/// Start this FluentMockServer.
///
/// The port.
/// The SSL support.
/// The .
[PublicAPI]
public static FluentMockServer Start([CanBeNull] int? port = 0, bool ssl = false)
{
return new FluentMockServer(new FluentMockServerSettings
{
Port = port,
UseSSL = ssl
});
}
///
/// Start this FluentMockServer.
///
/// The urls to listen on.
/// The .
[PublicAPI]
public static FluentMockServer Start(params string[] urls)
{
Check.NotEmpty(urls, nameof(urls));
return new FluentMockServer(new FluentMockServerSettings
{
Urls = urls
});
}
///
/// Start this FluentMockServer with the admin interface.
///
/// The port.
/// The SSL support.
/// The .
[PublicAPI]
public static FluentMockServer StartWithAdminInterface(int? port = 0, bool ssl = false)
{
return new FluentMockServer(new FluentMockServerSettings
{
Port = port,
UseSSL = ssl,
StartAdminInterface = true
});
}
///
/// Start this FluentMockServer with the admin interface.
///
/// The urls.
/// The .
[PublicAPI]
public static FluentMockServer StartWithAdminInterface(params string[] urls)
{
Check.NotEmpty(urls, nameof(urls));
return new FluentMockServer(new FluentMockServerSettings
{
Urls = urls,
StartAdminInterface = true
});
}
///
/// Start this FluentMockServer with the admin interface and read static mappings.
///
/// The urls.
/// The .
[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 NET45
_httpServer = new OwinSelfHost(_options, Urls);
#else
_httpServer = new AspNetCoreSelfHost(_options, Urls);
#endif
Ports = _httpServer.Ports;
_httpServer.StartAsync();
if (settings.StartAdminInterface == true)
{
InitAdmin();
}
if (settings.ReadStaticMappings == true)
{
ReadStaticMappings();
}
}
///
/// Adds the catch all mapping.
///
[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" }));
}
///
/// Stop this server.
///
[PublicAPI]
public void Stop()
{
_httpServer?.StopAsync();
}
///
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
///
public void Dispose()
{
if (_httpServer != null && _httpServer.IsStarted)
{
_httpServer.StopAsync();
}
}
///
/// Resets LogEntries and Mappings.
///
[PublicAPI]
public void Reset()
{
ResetLogEntries();
ResetMappings();
}
///
/// Resets the LogEntries.
///
[PublicAPI]
public void ResetLogEntries()
{
lock (((ICollection)_options.LogEntries).SyncRoot)
{
_options.LogEntries.Clear();
}
}
///
/// Deletes the mapping.
///
/// The unique identifier.
[PublicAPI]
public bool DeleteLogEntry(Guid guid)
{
lock (((ICollection)_options.LogEntries).SyncRoot)
{
// Check a logentry exists with the same GUID, if so, remove it.
var existing = _options.LogEntries.FirstOrDefault(m => m.Guid == guid);
if (existing != null)
{
_options.LogEntries.Remove(existing);
return true;
}
return false;
}
}
///
/// Resets the Mappings.
///
[PublicAPI]
public void ResetMappings()
{
lock (((ICollection)_options.Mappings).SyncRoot)
{
_options.Mappings = _options.Mappings.Where(m => m.Provider is DynamicResponseProvider).ToList();
}
}
///
/// Deletes the mapping.
///
/// The unique identifier.
[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;
}
}
///
/// The add request processing delay.
///
///
/// The delay.
///
[PublicAPI]
public void AddGlobalProcessingDelay(TimeSpan delay)
{
lock (_syncRoot)
{
_options.RequestProcessingDelay = delay;
}
}
///
/// Allows the partial mapping.
///
[PublicAPI]
public void AllowPartialMapping()
{
lock (_syncRoot)
{
_options.AllowPartialMapping = true;
}
}
///
/// Sets the basic authentication.
///
/// The username.
/// The password.
[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 + "$");
}
///
/// The given.
///
/// The request matcher.
/// The .
[PublicAPI]
public IRespondWithAProvider Given(IRequestMatcher requestMatcher)
{
return new RespondWithAProvider(RegisterMapping, requestMatcher);
}
///
/// The register mapping.
///
///
/// The mapping.
///
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);
}
}
//private async void HandleRequestOld(IOwinContext ctx)
//{
// if (_requestProcessingDelay > TimeSpan.Zero)
// {
// lock (_syncRoot)
// {
// Task.Delay(_requestProcessingDelay.Value).Wait();
// }
// }
// var request = _requestMapper.MapAsync(ctx.Request);
// ResponseMessage response = null;
// Mapping targetMapping = null;
// RequestMatchResult requestMatchResult = null;
// try
// {
// var mappings = _mappings
// .Select(m => new { Mapping = m, MatchResult = m.IsRequestHandled(request) })
// .ToList();
// if (_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 && _authorizationMatcher != null)
// {
// string authorization;
// bool present = request.Headers.TryGetValue("Authorization", out authorization);
// if (!present || _authorizationMatcher.IsMatch(authorization) < 1.0)
// {
// response = new ResponseMessage { StatusCode = 401 };
// return;
// }
// }
// response = await targetMapping.ResponseTo(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);
// _responseMapper.MapAsync(response, ctx.Response);
// ctx.Response.Close();
// }
//}
}
}