using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; using JetBrains.Annotations; using WireMock.Http; using WireMock.Matchers.Request; using WireMock.Validation; namespace WireMock.Server { /// /// The fluent mock server. /// public partial class FluentMockServer { /// /// The _http server. /// private readonly TinyHttpServer _httpServer; /// /// The _mappings. /// private readonly IList _mappings = new List(); /// /// The _request logs. /// private readonly IList _requestLogs = new List(); /// /// The _request mapper. /// private readonly HttpListenerRequestMapper _requestMapper = new HttpListenerRequestMapper(); /// /// The _response mapper. /// private readonly HttpListenerResponseMapper _responseMapper = new HttpListenerResponseMapper(); /// /// The _sync root. /// private readonly object _syncRoot = new object(); /// /// The _request processing delay. /// private TimeSpan _requestProcessingDelay = TimeSpan.Zero; /// /// Gets the port. /// public int Port { get; } /// /// Gets the request logs. /// public IEnumerable RequestLogs { get { lock (((ICollection)_requestLogs).SyncRoot) { return new ReadOnlyCollection(_requestLogs); } } } /// /// Gets the routes. /// public IEnumerable Mappings { get { lock (((ICollection)_mappings).SyncRoot) { return new ReadOnlyCollection(_mappings); } } } /// /// Start this FluentMockServer. /// /// The port. /// The SSL support. /// The . [PublicAPI] public static FluentMockServer Start(int port = 0, bool ssl = false) { Check.Condition(port, p => p >= 0, nameof(port)); if (port == 0) port = Ports.FindFreeTcpPort(); return new FluentMockServer(false, port, ssl); } /// /// Start this FluentMockServer with the admin interface. /// /// The port. /// The SSL support. /// The . [PublicAPI] public static FluentMockServer StartWithAdminInterface(int port = 0, bool ssl = false) { Check.Condition(port, p => p >= 0, nameof(port)); if (port == 0) port = Ports.FindFreeTcpPort(); return new FluentMockServer(true, port, ssl); } private FluentMockServer(bool startAdmin, int port, bool ssl) { string protocol = ssl ? "https" : "http"; _httpServer = new TinyHttpServer(protocol + "://localhost:" + port + "/", HandleRequestAsync); Port = port; _httpServer.Start(); if (startAdmin) { InitAdmin(); } } /// /// Stop this server. /// public void Stop() { _httpServer.Stop(); } /// /// The reset. /// public void Reset() { lock (((ICollection)_requestLogs).SyncRoot) { _requestLogs.Clear(); } lock (((ICollection)_mappings).SyncRoot) { _mappings.Clear(); } } /// /// The search logs for. /// /// /// The matcher. /// /// /// The . /// public IEnumerable SearchLogsFor(IRequestMatcher spec) { lock (((ICollection)_requestLogs).SyncRoot) { return _requestLogs.Where(spec.IsMatch); } } /// /// The add request processing delay. /// /// /// The delay. /// public void AddRequestProcessingDelay(TimeSpan delay) { lock (_syncRoot) { _requestProcessingDelay = delay; } } /// /// The given. /// /// The request matcher. /// The . public IRespondWithAProvider Given(IRequestMatcher requestMatcher) { return new RespondWithAProvider(RegisterMapping, requestMatcher); } /// /// The register mapping. /// /// /// The mapping. /// private void RegisterMapping(Mapping mapping) { lock (((ICollection)_mappings).SyncRoot) { _mappings.Add(mapping); } } /// /// The log request. /// /// /// The request. /// private void LogRequest(RequestMessage requestMessage) { lock (((ICollection)_requestLogs).SyncRoot) { _requestLogs.Add(requestMessage); } } /// /// The handle request. /// /// The HttpListenerContext. private async void HandleRequestAsync(HttpListenerContext ctx) { lock (_syncRoot) { Task.Delay(_requestProcessingDelay).Wait(); } var request = _requestMapper.Map(ctx.Request); LogRequest(request); try { var targetRoute = _mappings.FirstOrDefault(route => route.IsRequestHandled(request)); if (targetRoute == null) { ctx.Response.StatusCode = 404; byte[] content = Encoding.UTF8.GetBytes("Mock Server: page not found"); ctx.Response.OutputStream.Write(content, 0, content.Length); } else { var response = await targetRoute.ResponseTo(request); _responseMapper.Map(response, ctx.Response); } } catch (Exception ex) { ctx.Response.StatusCode = 500; byte[] content = Encoding.UTF8.GetBytes(ex.ToString()); ctx.Response.OutputStream.Write(content, 0, content.Length); } finally { ctx.Response.Close(); } } } }