diff --git a/src/Components/WebView/WebView/src/StaticContentProvider.cs b/src/Components/WebView/WebView/src/StaticContentProvider.cs index 8fb7fd80cc8b..ece1d44fbc2f 100644 --- a/src/Components/WebView/WebView/src/StaticContentProvider.cs +++ b/src/Components/WebView/WebView/src/StaticContentProvider.cs @@ -27,6 +27,8 @@ public bool TryGetResponseContent(string requestUri, bool allowFallbackOnHostPag { var relativePath = _appBaseUri.MakeRelativeUri(fileUri).ToString(); + relativePath = Uri.UnescapeDataString(relativePath); + // Content in the file provider takes first priority // Next we may fall back on supplying the host page to support deep linking // If there's no match, fall back on serving embedded framework content diff --git a/src/Components/WebView/WebView/test/StaticContentProviderTests.cs b/src/Components/WebView/WebView/test/StaticContentProviderTests.cs index 24251eb0f77d..fcf7149a31ec 100644 --- a/src/Components/WebView/WebView/test/StaticContentProviderTests.cs +++ b/src/Components/WebView/WebView/test/StaticContentProviderTests.cs @@ -41,6 +41,38 @@ public void TryGetResponseContentReturnsCorrectContentTypeForNonPhysicalFile() Assert.Equal("text/css", contentTypeValue); } + [Fact] + public void TryGetResponseContentCanHandleWhitespaceInFileName() + { + // Arrange + const string cssFilePath = "file with whitespace.css"; + const string cssFileContent = "this is css"; + var inMemoryFileProvider = new InMemoryFileProvider( + new Dictionary + { + { cssFilePath, cssFileContent }, + }); + var appBase = "fake://0.0.0.0/"; + var scp = new StaticContentProvider(inMemoryFileProvider, new Uri(appBase), "fakehost.html"); + + // Act + Assert.True(scp.TryGetResponseContent( + requestUri: appBase + Uri.EscapeDataString(cssFilePath), + allowFallbackOnHostPage: false, + out var statusCode, + out var statusMessage, + out var content, + out var headers)); + + // Assert + var contentString = new StreamReader(content).ReadToEnd(); + Assert.Equal(200, statusCode); + Assert.Equal("OK", statusMessage); + Assert.Equal("this is css", contentString); + Assert.True(headers.TryGetValue("Content-Type", out var contentTypeValue)); + Assert.Equal("text/css", contentTypeValue); + } + private sealed class InMemoryFileProvider : IFileProvider { public InMemoryFileProvider(IDictionary filePathsAndContents)