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(); // } //} } }