mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-04-23 16:58:27 +02:00
Improved relative path checking based on file existence (#411)
* Improved relative path checking based on file existence If the file exists at the relative path, then use it. If not, then use the path as is. * Apply File.Exists logic to ReadResponseBodyAsString as well * Make path handling more robust since path is user defined * Unit tests for relative path feature * Replace all back and forward slashes with system dependent DirectorySeparatorChar * Attempt fix broken directory separator chars for Unix platforms * Revert wrapping GetMappingFolder with CleanPath * Move CleanPath logic to its own class * Remove whitespace * Remove more whitespace * Improve CleanPath method * Move PathUtils tests to separate class Add another test to ResponseWithBodyFromFileTests * Fix Response_ProvideResponse_WithBodyFromFile_InAdminMappingFolder * Debug Linux CI build * Debug Linux CI * print all files from admin mappings folder * Debug CleanPath * Fix removed leading directory separator char in Linux breaks file path Remove debugging statements * Move combine to PathUtils * PathUtils + PathUtilsTests * Remove replicated (3x) tests throughout ResponseWithBodyFromFileTests Co-authored-by: Stef Heyenrath <Stef.Heyenrath@gmail.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using WireMock.Util;
|
||||||
using WireMock.Validation;
|
using WireMock.Validation;
|
||||||
|
|
||||||
namespace WireMock.Handlers
|
namespace WireMock.Handlers
|
||||||
@@ -80,20 +81,20 @@ namespace WireMock.Handlers
|
|||||||
public byte[] ReadResponseBodyAsFile(string path)
|
public byte[] ReadResponseBodyAsFile(string path)
|
||||||
{
|
{
|
||||||
Check.NotNullOrEmpty(path, nameof(path));
|
Check.NotNullOrEmpty(path, nameof(path));
|
||||||
|
path = PathUtils.CleanPath(path);
|
||||||
// In case the path is a filename, the path will be adjusted to the MappingFolder.
|
// If the file exists at the given path relative to the MappingsFolder, then return that.
|
||||||
// Else the path will just be as-is.
|
// Else the path will just be as-is.
|
||||||
return File.ReadAllBytes(Path.GetFileName(path) == path ? Path.Combine(GetMappingFolder(), path) : path);
|
return File.ReadAllBytes(File.Exists(PathUtils.Combine(GetMappingFolder(), path)) ? PathUtils.Combine(GetMappingFolder(), path) : path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IFileSystemHandler.ReadResponseBodyAsString"/>
|
/// <inheritdoc cref="IFileSystemHandler.ReadResponseBodyAsString"/>
|
||||||
public string ReadResponseBodyAsString(string path)
|
public string ReadResponseBodyAsString(string path)
|
||||||
{
|
{
|
||||||
Check.NotNullOrEmpty(path, nameof(path));
|
Check.NotNullOrEmpty(path, nameof(path));
|
||||||
|
path = PathUtils.CleanPath(path);
|
||||||
// In case the path is a filename, the path will be adjusted to the MappingFolder.
|
// In case the path is a filename, the path will be adjusted to the MappingFolder.
|
||||||
// Else the path will just be as-is.
|
// Else the path will just be as-is.
|
||||||
return File.ReadAllText(Path.GetFileName(path) == path ? Path.Combine(GetMappingFolder(), path) : path);
|
return File.ReadAllText(File.Exists(PathUtils.Combine(GetMappingFolder(), path)) ? PathUtils.Combine(GetMappingFolder(), path) : path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IFileSystemHandler.FileExists"/>
|
/// <inheritdoc cref="IFileSystemHandler.FileExists"/>
|
||||||
|
|||||||
36
src/WireMock.Net/Util/PathUtils.cs
Normal file
36
src/WireMock.Net/Util/PathUtils.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace WireMock.Util
|
||||||
|
{
|
||||||
|
internal static class PathUtils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Robust handling of the user defined path.
|
||||||
|
/// Also supports Unix and Windows platforms
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path to clean</param>
|
||||||
|
public static string CleanPath(string path)
|
||||||
|
{
|
||||||
|
return path?.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes leading directory separator chars from the filepath, which could break Path.Combine
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path to remove the loading DirectorySeparatorChars</param>
|
||||||
|
public static string RemoveLeadingDirectorySeparators(string path)
|
||||||
|
{
|
||||||
|
return path?.TrimStart(new[] { Path.DirectorySeparatorChar });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Combine two paths
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="root">The root path</param>
|
||||||
|
/// <param name="path">The path</param>
|
||||||
|
public static string Combine(string root, string path)
|
||||||
|
{
|
||||||
|
return Path.Combine(root, RemoveLeadingDirectorySeparators(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
@@ -13,11 +15,10 @@ namespace WireMock.Net.Tests.ResponseBuilders
|
|||||||
public class ResponseWithBodyFromFileTests
|
public class ResponseWithBodyFromFileTests
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Response_ProvideResponse_WithBodyFromFile()
|
public async Task Response_ProvideResponse_WithBodyFromFile_AbsolutePath()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var server = WireMockServer.Start();
|
var server = WireMockServer.Start();
|
||||||
|
|
||||||
string path = Path.Combine(Directory.GetCurrentDirectory(), "__admin", "mappings", "MyXmlResponse.xml");
|
string path = Path.Combine(Directory.GetCurrentDirectory(), "__admin", "mappings", "MyXmlResponse.xml");
|
||||||
|
|
||||||
server
|
server
|
||||||
@@ -36,14 +37,68 @@ namespace WireMock.Net.Tests.ResponseBuilders
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var response1 = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");
|
var response = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");
|
||||||
var response2 = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");
|
|
||||||
var response3 = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");
|
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
response1.Should().Contain("<hello>world</hello>");
|
response.Should().Contain("<hello>world</hello>");
|
||||||
response2.Should().Contain("<hello>world</hello>");
|
}
|
||||||
response3.Should().Contain("<hello>world</hello>");
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Response_ProvideResponse_WithBodyFromFile_InSubDirectory()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = WireMockServer.Start();
|
||||||
|
string path = @"subdirectory/MyXmlResponse.xml";
|
||||||
|
|
||||||
|
server
|
||||||
|
.Given(
|
||||||
|
Request
|
||||||
|
.Create()
|
||||||
|
.UsingGet()
|
||||||
|
.WithPath("/v1/content")
|
||||||
|
)
|
||||||
|
.RespondWith(
|
||||||
|
Response
|
||||||
|
.Create()
|
||||||
|
.WithStatusCode(HttpStatusCode.OK)
|
||||||
|
.WithHeader("Content-Type", "application/xml")
|
||||||
|
.WithBodyFromFile(path)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
response.Should().Contain("<hello>world</hello>");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Response_ProvideResponse_WithBodyFromFile_InAdminMappingFolder()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = WireMockServer.Start();
|
||||||
|
string path = @"MyXmlResponse.xml";
|
||||||
|
|
||||||
|
server
|
||||||
|
.Given(
|
||||||
|
Request
|
||||||
|
.Create()
|
||||||
|
.UsingGet()
|
||||||
|
.WithPath("/v1/content")
|
||||||
|
)
|
||||||
|
.RespondWith(
|
||||||
|
Response
|
||||||
|
.Create()
|
||||||
|
.WithStatusCode(HttpStatusCode.OK)
|
||||||
|
.WithHeader("Content-Type", "application/xml")
|
||||||
|
.WithBodyFromFile(path)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
response.Should().Contain("<hello>world</hello>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
44
test/WireMock.Net.Tests/Util/PathUtilsTests.cs
Normal file
44
test/WireMock.Net.Tests/Util/PathUtilsTests.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using NFluent;
|
||||||
|
using System.IO;
|
||||||
|
using WireMock.Util;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace WireMock.Net.Tests.Util
|
||||||
|
{
|
||||||
|
public class PathUtilsTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData(@"subdirectory/MyXmlResponse.xml")]
|
||||||
|
[InlineData(@"subdirectory\MyXmlResponse.xml")]
|
||||||
|
public void PathUtils_CleanPath(string path)
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
var cleanPath = PathUtils.CleanPath(path);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Check.That(cleanPath).Equals("subdirectory" + Path.DirectorySeparatorChar + "MyXmlResponse.xml");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(null, null)]
|
||||||
|
[InlineData("", "")]
|
||||||
|
[InlineData("a", "a")]
|
||||||
|
[InlineData(@"/", "")]
|
||||||
|
[InlineData(@"//", "")]
|
||||||
|
[InlineData(@"//a", "a")]
|
||||||
|
[InlineData(@"\", "")]
|
||||||
|
[InlineData(@"\\", "")]
|
||||||
|
[InlineData(@"\\a", "a")]
|
||||||
|
public void PathUtils_CleanPath_RemoveLeadingDirectorySeparators(string path, string expected)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var cleanPath = PathUtils.CleanPath(path);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var withoutDirectorySeparators = PathUtils.RemoveLeadingDirectorySeparators(cleanPath);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Check.That(withoutDirectorySeparators).Equals(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -83,6 +83,9 @@
|
|||||||
<None Update="__admin\mappings\MyXmlResponse.xml">
|
<None Update="__admin\mappings\MyXmlResponse.xml">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
|
<None Update="__admin\mappings\subdirectory\MyXmlResponse.xml">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<xml>
|
||||||
|
<hello>world</hello>
|
||||||
|
</xml>
|
||||||
Reference in New Issue
Block a user