Compare commits

...

8 Commits

Author SHA1 Message Date
Stef Heyenrath
454051568a 1.0.3.20 2018-05-29 22:01:15 +02:00
Stef Heyenrath
2b498f45cb Revert PortUtil.cs changes (#147) 2018-05-29 21:58:56 +02:00
Stef Heyenrath
2fcfda49c7 Update changelog 2018-05-28 15:13:34 +02:00
Stef Heyenrath
7ef0c1d68b 1.0.3.19 2018-05-28 08:43:41 +02:00
Bob Paul
297743a19a Fix code for .NET Core 2 so that server has cancellation token, and Stop method waits for shutdown. This resolves issues when using .NET Core 2 and starting/stopping server instances. (#145) 2018-05-28 08:30:18 +02:00
Stef Heyenrath
0640c88bcd Fix ConcurrentDictionary (#129) (#144) 2018-05-28 08:08:18 +02:00
Stef Heyenrath
dc39f91205 1.0.3.18 2018-05-25 21:43:29 +02:00
Stef Heyenrath
eda71bd725 Allow all headers to be set as Response headers (#142)
* Allow all headers to be set as Response headers

* RunTestDifferentPort

* Fix Proxy_Should_change_absolute_location_header_in_proxied_response test

* Fix OwinResponseMapper

* Fix test

* 1.0.3.18
2018-05-25 21:04:58 +02:00
15 changed files with 164 additions and 133 deletions

View File

@@ -1,3 +1,37 @@
# 1.0.3.20 (29 May 2018)
- [#147](https://github.com/WireMock-Net/WireMock.Net/pull/147) - Revert PortUtil.cs changes contributed by Stef Heyenrath ([StefH](https://github.com/StefH))
- [#146](https://github.com/WireMock-Net/WireMock.Net/issues/146) - Hang possibly due to Windows firewall prompt
- [#129](https://github.com/WireMock-Net/WireMock.Net/issues/129) - Random test failures between WireMock.Net 1.0.3.1 and 1.0.3.2
Commits: 2fcfda49c7...2b498f45cb
# 1.0.3.19 (28 May 2018)
- [#129](https://github.com/WireMock-Net/WireMock.Net/issues/129) - Random test failures between WireMock.Net 1.0.3.1 and 1.0.3.2
- [#145](https://github.com/WireMock-Net/WireMock.Net/pull/145) - Cancellation token not passed to server instance in .NET Core 2 contributed by Bob Paul ([Bob11327](https://github.com/Bob11327)) +fix
- [#144](https://github.com/WireMock-Net/WireMock.Net/pull/144) - Fix ConcurrentDictionary (#129) contributed by Stef Heyenrath ([StefH](https://github.com/StefH))
Commits: ...
# 1.0.3.18 (25 May 2018)
- [#142](https://github.com/WireMock-Net/WireMock.Net/pull/142) - Allow all headers to be set as Response headers contributed by Stef Heyenrath ([StefH](https://github.com/StefH))
- [#140](https://github.com/WireMock-Net/WireMock.Net/issues/140) - Question: Why the Microsoft.Owin.Host.HttpListener is not referenced in the dll, which uses WireMock?
- [#139](https://github.com/WireMock-Net/WireMock.Net/issues/139) - Wiki link https://github.com/StefH/WireMock.Net/wiki/Record-(via-proxy)-and-Save is dead
- [#137](https://github.com/WireMock-Net/WireMock.Net/issues/137) - Question: How to specify Transfer-Encoding response header?
- [#136](https://github.com/WireMock-Net/WireMock.Net/issues/136) - Question: Does the WireMock send Content-Length response header
- [#132](https://github.com/WireMock-Net/WireMock.Net/issues/132) - LogEntries not being recorded on subsequent tests
- [#127](https://github.com/WireMock-Net/WireMock.Net/issues/127) - Question: Stub priority - Most recent stub is not always used
- [#126](https://github.com/WireMock-Net/WireMock.Net/issues/126) - Question: UsingHead always returns 0 for Content-Length header even when explicitly specified
- [#122](https://github.com/WireMock-Net/WireMock.Net/issues/122) - WireMock.Net not responding in unit tests - same works in console application
- [#97](https://github.com/WireMock-Net/WireMock.Net/issues/97) - Request matching logic is not practical
Commits: eda71bd725...eda71bd725
# 1.0.3.17 (16 May 2018)
- [#138](https://github.com/WireMock-Net/WireMock.Net/pull/138) - Added Negate matcher logic contributed by Stef Heyenrath ([StefH](https://github.com/StefH))

View File

@@ -1,5 +1,5 @@
https://github.com/GitTools/GitReleaseNotes
GitReleaseNotes.exe . /OutputFile CHANGELOG.md /Version 1.0.3.17
GitReleaseNotes.exe . /OutputFile CHANGELOG.md /Version 1.0.3.20
GitReleaseNotes.exe . /OutputFile CHANGELOG.md /allTags

View File

@@ -1,4 +1,10 @@
using Newtonsoft.Json;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Server;
using WireMock.Settings;
@@ -8,6 +14,9 @@ namespace WireMock.Net.Console.Proxy.NETCoreApp2
{
static void Main(string[] args)
{
RunTestDifferentPort().Wait(20000); // prints "1"
RunTestDifferentPort().Wait(20000); // prints "1"
var server = FluentMockServer.Start(new FluentMockServerSettings
{
Urls = new[] { "http://localhost:9091", "https://localhost:9443" },
@@ -32,5 +41,22 @@ namespace WireMock.Net.Console.Proxy.NETCoreApp2
System.Console.ReadKey();
server.Stop();
}
private static async Task RunTestDifferentPort()
{
var server = FluentMockServer.Start();
server.Given(Request.Create().WithPath("/").UsingGet())
.RespondWith(Response.Create().WithStatusCode(200).WithBody("Hello"));
Thread.Sleep(1000);
var response = await new HttpClient().GetAsync(server.Urls[0]);
response.EnsureSuccessStatusCode();
System.Console.WriteLine("RunTestDifferentPort - server.LogEntries.Count() = " + server.LogEntries.Count());
server.Stop();
}
}
}

View File

@@ -3,7 +3,7 @@
<PropertyGroup>
<Description>Lightweight StandAlone Http Mocking Server for .Net.</Description>
<AssemblyTitle>WireMock.Net.StandAlone</AssemblyTitle>
<Version>1.0.3.17</Version>
<Version>1.0.3.20</Version>
<Authors>Stef Heyenrath</Authors>
<TargetFrameworks>net452;net46;netstandard1.3;netstandard2.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>

View File

@@ -94,8 +94,6 @@ namespace WireMock.Owin
#endif
.Build();
IsStarted = true;
return Task.Run(() =>
{
StartServers();
@@ -112,7 +110,7 @@ namespace WireMock.Owin
_host.Run(_cts.Token);
#else
_logger.Info("WireMock.Net server using netstandard2.0");
_host.Run();
_host.RunAsync(_cts.Token).Wait();
#endif
}
catch (Exception e)

View File

@@ -24,29 +24,41 @@ namespace WireMock.Owin
// https://msdn.microsoft.com/en-us/library/78h415ay(v=vs.110).aspx
#if !NETSTANDARD
private static readonly IDictionary<string, Action<IOwinResponse, WireMockList<string>>> RestrictedResponseHeaders = new Dictionary<string, Action<IOwinResponse, WireMockList<string>>>(StringComparer.OrdinalIgnoreCase) {
private static readonly IDictionary<string, Action<IOwinResponse, WireMockList<string>>> ResponseHeadersToFix = new Dictionary<string, Action<IOwinResponse, WireMockList<string>>>(StringComparer.OrdinalIgnoreCase) {
#else
private static readonly IDictionary<string, Action<HttpResponse, WireMockList<string>>> RestrictedResponseHeaders = new Dictionary<string, Action<HttpResponse, WireMockList<string>>>(StringComparer.OrdinalIgnoreCase) {
private static readonly IDictionary<string, Action<HttpResponse, WireMockList<string>>> ResponseHeadersToFix = new Dictionary<string, Action<HttpResponse, WireMockList<string>>>(StringComparer.OrdinalIgnoreCase) {
#endif
{ HttpKnownHeaderNames.Accept, null },
{ HttpKnownHeaderNames.Connection, null },
{ HttpKnownHeaderNames.ContentLength, null },
{ HttpKnownHeaderNames.ContentType, (r, v) => r.ContentType = v.FirstOrDefault() },
{ HttpKnownHeaderNames.Date, null },
{ HttpKnownHeaderNames.Expect, null },
{ HttpKnownHeaderNames.Host, null },
{ HttpKnownHeaderNames.IfModifiedSince, null },
{ HttpKnownHeaderNames.KeepAlive, null },
{ HttpKnownHeaderNames.Range, null },
{ HttpKnownHeaderNames.Referer, null },
{ HttpKnownHeaderNames.TransferEncoding, null },
{ HttpKnownHeaderNames.UserAgent, null },
{ HttpKnownHeaderNames.ProxyConnection, null },
{ HttpKnownHeaderNames.WWWAuthenticate, null }
{ HttpKnownHeaderNames.ContentType, (r, v) => r.ContentType = v.FirstOrDefault() }
};
private void SetResponseHeaders(ResponseMessage responseMessage
#if !NETSTANDARD
, IOwinResponse response
#else
, HttpResponse response
#endif
)
{
// Set headers
foreach (var pair in responseMessage.Headers)
{
if (ResponseHeadersToFix.ContainsKey(pair.Key))
{
ResponseHeadersToFix[pair.Key]?.Invoke(response, pair.Value);
}
else
{
#if !NETSTANDARD
response.Headers.AppendValues(pair.Key, pair.Value.ToArray());
#else
response.Headers.Append(pair.Key, pair.Value.ToArray());
#endif
}
}
}
/// <summary>
/// MapAsync ResponseMessage to OwinResponse
/// Map ResponseMessage to OwinResponse/HttpResponse
/// </summary>
/// <param name="responseMessage"></param>
/// <param name="response"></param>
@@ -60,57 +72,31 @@ namespace WireMock.Owin
{
response.StatusCode = responseMessage.StatusCode;
// Set headers
foreach (var pair in responseMessage.Headers)
{
if (RestrictedResponseHeaders.ContainsKey(pair.Key))
{
RestrictedResponseHeaders[pair.Key]?.Invoke(response, pair.Value);
}
else
{
#if !NETSTANDARD
response.Headers.AppendValues(pair.Key, pair.Value.ToArray());
#else
response.Headers.Append(pair.Key, pair.Value.ToArray());
#endif
}
}
if (responseMessage.Body == null && responseMessage.BodyAsBytes == null && responseMessage.BodyAsFile == null && responseMessage.BodyAsJson == null)
{
return;
}
byte[] bytes = null;
if (responseMessage.BodyAsBytes != null)
{
await response.Body.WriteAsync(responseMessage.BodyAsBytes, 0, responseMessage.BodyAsBytes.Length);
return;
bytes = responseMessage.BodyAsBytes;
}
if (responseMessage.BodyAsFile != null)
else if (responseMessage.BodyAsFile != null)
{
byte[] bytes = File.ReadAllBytes(responseMessage.BodyAsFile);
await response.Body.WriteAsync(bytes, 0, bytes.Length);
return;
bytes = File.ReadAllBytes(responseMessage.BodyAsFile);
}
if (responseMessage.BodyAsJson != null)
else if (responseMessage.BodyAsJson != null)
{
Formatting formatting = responseMessage.BodyAsJsonIndented == true ? Formatting.Indented : Formatting.None;
string jsonBody = JsonConvert.SerializeObject(responseMessage.BodyAsJson, new JsonSerializerSettings { Formatting = formatting, NullValueHandling = NullValueHandling.Ignore });
using (var writer = new StreamWriter(response.Body, responseMessage.BodyEncoding ?? _utf8NoBom))
{
await writer.WriteAsync(jsonBody);
}
return;
bytes = (responseMessage.BodyEncoding ?? _utf8NoBom).GetBytes(jsonBody);
}
else if (responseMessage.Body != null)
{
bytes = (responseMessage.BodyEncoding ?? _utf8NoBom).GetBytes(responseMessage.Body);
}
using (var writer = new StreamWriter(response.Body, responseMessage.BodyEncoding ?? _utf8NoBom))
SetResponseHeaders(responseMessage, response);
if (bytes != null)
{
await writer.WriteAsync(responseMessage.Body);
await response.Body.WriteAsync(bytes, 0, bytes.Length);
}
}
}

View File

@@ -58,7 +58,7 @@ namespace WireMock.Owin
// Set start
if (!_options.Scenarios.ContainsKey(mapping.Scenario) && mapping.IsStartState)
{
_options.Scenarios.Add(mapping.Scenario, null);
_options.Scenarios.TryAdd(mapping.Scenario, null);
}
}

View File

@@ -1,6 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using WireMock.Logging;
using WireMock.Matchers;
@@ -23,7 +22,9 @@ namespace WireMock.Owin
public bool AllowPartialMapping { get; set; }
public IDictionary<Guid, Mapping> Mappings { get; } = new ConcurrentDictionary<Guid, Mapping>();
public ConcurrentDictionary<Guid, Mapping> Mappings { get; } = new ConcurrentDictionary<Guid, Mapping>(); // Checked
public ConcurrentDictionary<string, object> Scenarios { get; } = new ConcurrentDictionary<string, object>(); // Checked
public ObservableCollection<LogEntry> LogEntries { get; } = new ConcurentObservableCollection<LogEntry>();
@@ -31,8 +32,6 @@ namespace WireMock.Owin
public int? MaxRequestLogCount { get; set; }
public IDictionary<string, object> Scenarios { get; } = new ConcurrentDictionary<string, object>();
#if !NETSTANDARD
public Action<IAppBuilder> PreWireMockMiddlewareInit { get; set; }

View File

@@ -1,5 +1,4 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WireMock.Util;
@@ -8,14 +7,14 @@ using WireMock.Validation;
namespace WireMock
{
/// <summary>
/// The response.
/// The ResponseMessage.
/// </summary>
public class ResponseMessage
{
/// <summary>
/// Gets the headers.
/// </summary>
public IDictionary<string, WireMockList<string>> Headers { get; set; } = new ConcurrentDictionary<string, WireMockList<string>>();
public IDictionary<string, WireMockList<string>> Headers { get; set; } = new Dictionary<string, WireMockList<string>>();
/// <summary>
/// Gets or sets the status code.

View File

@@ -210,7 +210,7 @@ namespace WireMock.Server
if (settings.SaveMapping)
{
var mapping = ToMapping(requestMessage, responseMessage, settings.BlackListedHeaders ?? new string[] { });
_options.Mappings.Add(mapping.Guid, mapping);
_options.Mappings.TryAdd(mapping.Guid, mapping);
if (settings.SaveMappingToFile)
{
@@ -577,7 +577,7 @@ namespace WireMock.Server
#region Scenarios
private ResponseMessage ScenariosGet(RequestMessage requestMessage)
{
var scenarios = Scenarios.Select(s => new
var scenarios = Scenarios.ToArray().Select(s => new
{
Name = s.Key,
Started = s.Value != null,

View File

@@ -56,7 +56,7 @@ namespace WireMock.Server
/// Gets the scenarios.
/// </summary>
[PublicAPI]
public IDictionary<string, object> Scenarios => new ConcurrentDictionary<string, object>(_options.Scenarios);
public ConcurrentDictionary<string, object> Scenarios => new ConcurrentDictionary<string, object>(_options.Scenarios); // Checked
#region Start/Stop
/// <summary>
@@ -195,11 +195,13 @@ namespace WireMock.Server
{
throw new Exception($"Service start failed with error: {_httpServer.RunningException.Message}", _httpServer.RunningException);
}
// Respect start timeout setting by throwing TimeoutException
if (ctsStartTimeout.IsCancellationRequested)
{
throw new TimeoutException($"Service start timed out after {TimeSpan.FromMilliseconds(settings.StartTimeout)}");
}
ctsStartTimeout.Token.WaitHandle.WaitOne(ServerStartDelay);
}
}
@@ -246,7 +248,9 @@ namespace WireMock.Server
[PublicAPI]
public void Stop()
{
_httpServer?.StopAsync();
var result = _httpServer?.StopAsync();
if (result != null)
result.Wait(); //wait for stop to actually happen
}
#endregion
@@ -290,9 +294,9 @@ namespace WireMock.Server
[PublicAPI]
public void ResetMappings()
{
foreach (var nonAdmin in _options.Mappings.Where(m => !m.Value.IsAdminInterface))
foreach (var nonAdmin in _options.Mappings.ToArray().Where(m => !m.Value.IsAdminInterface))
{
_options.Mappings.Remove(nonAdmin);
_options.Mappings.TryRemove(nonAdmin.Key, out _);
}
}
@@ -306,7 +310,7 @@ namespace WireMock.Server
// Check a mapping exists with the same GUID, if so, remove it.
if (_options.Mappings.ContainsKey(guid))
{
return _options.Mappings.Remove(guid);
return _options.Mappings.TryRemove(guid, out _);
}
return false;
@@ -315,7 +319,7 @@ namespace WireMock.Server
private bool DeleteMapping(string path)
{
// Check a mapping exists with the same path, if so, remove it.
var mapping = _options.Mappings.FirstOrDefault(entry => string.Equals(entry.Value.Path, path, StringComparison.OrdinalIgnoreCase));
var mapping = _options.Mappings.ToArray().FirstOrDefault(entry => string.Equals(entry.Value.Path, path, StringComparison.OrdinalIgnoreCase));
return DeleteMapping(mapping.Key);
}
@@ -413,7 +417,7 @@ namespace WireMock.Server
}
else
{
_options.Mappings.Add(mapping.Guid, mapping);
_options.Mappings.TryAdd(mapping.Guid, mapping);
}
}
}

View File

@@ -3,7 +3,7 @@
<PropertyGroup>
<Description>Lightweight Http Mocking Server for .Net, inspired by WireMock from the Java landscape.</Description>
<AssemblyTitle>WireMock.Net</AssemblyTitle>
<Version>1.0.3.17</Version>
<Version>1.0.3.20</Version>
<Authors>Stef Heyenrath</Authors>
<TargetFrameworks>net452;net46;netstandard1.3;netstandard2.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
@@ -36,10 +36,6 @@
<Compile Remove="Util\NamedReaderWriterLocker.cs" />
</ItemGroup>
<ItemGroup>
<None Remove="Server\FluentMockServer.cs~RF44936b9f.TMP" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="11.1.0">
<PrivateAssets>All</PrivateAssets>

View File

@@ -5,7 +5,6 @@ using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using NFluent;
using WireMock.Matchers.Request;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Server;
@@ -190,29 +189,30 @@ namespace WireMock.Net.Tests
[Fact]
public async Task FluentMockServer_Proxy_Should_change_absolute_location_header_in_proxied_response()
{
// given
_serverForProxyForwarding = FluentMockServer.Start();
// Assign
var settings = new FluentMockServerSettings { AllowPartialMapping = false };
_serverForProxyForwarding = FluentMockServer.Start(settings);
_serverForProxyForwarding
.Given(Request.Create().WithPath("/*"))
.RespondWith(Response.Create()
.WithStatusCode(HttpStatusCode.Redirect)
.WithHeader("Location", _serverForProxyForwarding.Urls[0] + "testpath"));
_server = FluentMockServer.Start();
_server = FluentMockServer.Start(settings);
_server
.Given(Request.Create().WithPath("/*"))
.Given(Request.Create().WithPath("/prx"))
.RespondWith(Response.Create().WithProxy(_serverForProxyForwarding.Urls[0]));
// when
// Act
var requestMessage = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri(_server.Urls[0])
RequestUri = new Uri(_server.Urls[0] + "/prx")
};
var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false };
var response = await new HttpClient(httpClientHandler).SendAsync(requestMessage);
// then
// Assert
Check.That(response.Headers.Contains("Location")).IsTrue();
Check.That(response.Headers.GetValues("Location")).ContainsExactly(_server.Urls[0] + "testpath");
}

View File

@@ -351,8 +351,11 @@ namespace WireMock.Net.Tests
Check.That(responseAsBytes).ContainsExactly(new byte[] { 48, 49 });
}
public static IEnumerable<object[]> ValidMatchersForHelloServerJsonMessage =>
new List<object[]>
[Fact]
public async Task FluentMockServer_Should_respond_to_valid_matchers_when_sent_json()
{
// Assign
var validMatchersForHelloServerJsonMessage = new List<object[]>
{
new object[] { new WildcardMatcher("*Hello server*"), "application/json" },
new object[] { new WildcardMatcher("*Hello server*"), "text/plain" },
@@ -364,24 +367,25 @@ namespace WireMock.Net.Tests
new object[] { new JsonPathMatcher("$..[?(@.message == 'Hello server')]"), "text/plain" }
};
[Theory]
[MemberData(nameof(ValidMatchersForHelloServerJsonMessage))]
public async Task FluentMockServer_Should_respond_to_valid_matchers_when_sent_json(IMatcher matcher, string contentType)
{
// Assign
_server = FluentMockServer.Start();
_server
.Given(Request.Create().WithPath("/foo").WithBody(matcher))
.RespondWith(Response.Create().WithBody("Hello client"));
foreach (var item in validMatchersForHelloServerJsonMessage)
{
_server
.Given(Request.Create().WithPath("/foo").WithBody((IMatcher)item[0]))
.RespondWith(Response.Create().WithBody("Hello client"));
// Act
var content = new StringContent(jsonRequestMessage, Encoding.UTF8, contentType);
var response = await new HttpClient().PostAsync("http://localhost:" + _server.Ports[0] + "/foo", content);
// Act
var content = new StringContent(jsonRequestMessage, Encoding.UTF8, (string)item[1]);
var response = await new HttpClient().PostAsync("http://localhost:" + _server.Ports[0] + "/foo", content);
// Assert
var responseString = await response.Content.ReadAsStringAsync();
Check.That(responseString).Equals("Hello client");
// Assert
var responseString = await response.Content.ReadAsStringAsync();
Check.That(responseString).Equals("Hello client");
_server.ResetMappings();
_server.ResetLogEntries();
}
}
[Fact]
@@ -581,24 +585,6 @@ namespace WireMock.Net.Tests
Check.That(response).IsEqualTo("/fooBar");
}
[Fact]
public async Task FluentMockServer_Should_IgnoreRestrictedHeader()
{
// Assign
_server = FluentMockServer.Start();
_server
.Given(Request.Create().WithPath("/head").UsingHead())
.RespondWith(Response.Create().WithHeader("Content-Length", "1024"));
var request = new HttpRequestMessage(HttpMethod.Head, "http://localhost:" + _server.Ports[0] + "/head");
// Act
var response = await new HttpClient().SendAsync(request);
// Assert
Check.That(response.Content.Headers.GetValues("Content-Length")).ContainsExactly("0");
}
public void Dispose()
{
_server?.Stop();

View File

@@ -9,18 +9,21 @@ namespace WireMock.Net.Tests
{
private const string ClientIp = "::1";
[Fact]
public async void Response_Create_WithHeader_ContentLength()
[Theory]
[InlineData("Content-Length", "1024")]
[InlineData("Transfer-Encoding", "identity")]
[InlineData("Location", "http://test")]
public async void Response_Create_WithHeader(string headerName, string headerValue)
{
// Assign
var requestMock = new RequestMessage(new Uri("http://localhost/foo"), "PUT", ClientIp);
IResponseBuilder builder = Response.Create().WithHeader("Content-Length", "1024");
IResponseBuilder builder = Response.Create().WithHeader(headerName, headerValue);
// Act
var response = await builder.ProvideResponseAsync(requestMock);
// Assert
Check.That(response.Headers["Content-Length"].ToString()).Equals("1024");
Check.That(response.Headers[headerName].ToString()).Equals(headerValue);
}
}
}