diff --git a/Blazor.sln b/Blazor.sln index 4786973eeb..a2f844c0b0 100644 --- a/Blazor.sln +++ b/Blazor.sln @@ -37,7 +37,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "mono", "mono", "{7B5CAAB1-A EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Blazor.Mono", "runtime\Microsoft.Blazor.Mono\Microsoft.Blazor.Mono.csproj", "{39FEC72D-AF52-47A3-B63D-7BF0E4335248}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoSanity", "samples\MonoSanity\MonoSanity.csproj", "{7C53BB6B-5906-4753-B507-C9FCC2F7E5B7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoSanity", "samples\MonoSanity\MonoSanity.csproj", "{7C53BB6B-5906-4753-B507-C9FCC2F7E5B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Blazor.Server", "runtime\Microsoft.Blazor.Server\Microsoft.Blazor.Server.csproj", "{5A694793-3257-4D37-BB74-4A41B3894685}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{ADA3AE29-F6DE-49F6-8C7C-B321508CAE8E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Blazor.Mono.Test", "test\Microsoft.Blazor.Mono.Test\Microsoft.Blazor.Mono.Test.csproj", "{118484D3-3993-45CE-97C1-6F28A517529B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -61,15 +67,26 @@ Global {7C53BB6B-5906-4753-B507-C9FCC2F7E5B7}.Debug|Any CPU.Build.0 = Debug|Any CPU {7C53BB6B-5906-4753-B507-C9FCC2F7E5B7}.Release|Any CPU.ActiveCfg = Release|Any CPU {7C53BB6B-5906-4753-B507-C9FCC2F7E5B7}.Release|Any CPU.Build.0 = Release|Any CPU + {5A694793-3257-4D37-BB74-4A41B3894685}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5A694793-3257-4D37-BB74-4A41B3894685}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A694793-3257-4D37-BB74-4A41B3894685}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5A694793-3257-4D37-BB74-4A41B3894685}.Release|Any CPU.Build.0 = Release|Any CPU + {118484D3-3993-45CE-97C1-6F28A517529B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {118484D3-3993-45CE-97C1-6F28A517529B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {118484D3-3993-45CE-97C1-6F28A517529B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {118484D3-3993-45CE-97C1-6F28A517529B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {4C7EE25B-E9C7-4CA3-8357-77ADF9AAD20A} = {F5FDD4E5-6A52-4A86-BE5E-5E42CB1DC8DA} + {A1A3AEB6-A832-477E-8D91-A672CFE53801} = {ADA3AE29-F6DE-49F6-8C7C-B321508CAE8E} {7B5CAAB1-A3EB-44F7-87E3-A13ED89FC17D} = {B867E038-B3CE-43E3-9292-61568C46CDEB} {39FEC72D-AF52-47A3-B63D-7BF0E4335248} = {B867E038-B3CE-43E3-9292-61568C46CDEB} {7C53BB6B-5906-4753-B507-C9FCC2F7E5B7} = {F5FDD4E5-6A52-4A86-BE5E-5E42CB1DC8DA} + {5A694793-3257-4D37-BB74-4A41B3894685} = {B867E038-B3CE-43E3-9292-61568C46CDEB} + {118484D3-3993-45CE-97C1-6F28A517529B} = {ADA3AE29-F6DE-49F6-8C7C-B321508CAE8E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {504DA352-6788-4DC0-8705-82167E72A4D3} diff --git a/runtime/Microsoft.Blazor.Mono/Microsoft.Blazor.Mono.csproj b/runtime/Microsoft.Blazor.Mono/Microsoft.Blazor.Mono.csproj index 4c3ba4ce32..3e4a232d78 100644 --- a/runtime/Microsoft.Blazor.Mono/Microsoft.Blazor.Mono.csproj +++ b/runtime/Microsoft.Blazor.Mono/Microsoft.Blazor.Mono.csproj @@ -6,11 +6,15 @@ netcoreapp2.0 + + + + - mono:%(RecursiveDir)%(Filename)%(Extension) + mono.$([System.String]::Copy('/%(RecursiveDir)%(FileName)%(Extension)').Replace('\', '$').Replace('/', '$')) diff --git a/runtime/Microsoft.Blazor.Mono/MonoStaticFileProvider.cs b/runtime/Microsoft.Blazor.Mono/MonoStaticFileProvider.cs new file mode 100644 index 0000000000..231c217509 --- /dev/null +++ b/runtime/Microsoft.Blazor.Mono/MonoStaticFileProvider.cs @@ -0,0 +1,30 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Primitives; +using System; + +namespace Microsoft.Blazor.Mono +{ + public class MonoStaticFileProvider : IFileProvider + { + private EmbeddedFileProvider _embeddedFiles = new EmbeddedFileProvider( + typeof(MonoStaticFileProvider).Assembly, + "mono"); + + public IFileInfo GetFileInfo(string subpath) + { + // EmbeddedFileProvider can't find resources whose names include '/' (or '\'), + // so the resources in the assembly use '$' as a directory separator + var possibleResourceName = subpath.Replace('/', '$'); + return _embeddedFiles.GetFileInfo(possibleResourceName); + } + + public IDirectoryContents GetDirectoryContents(string subpath) + => throw new NotImplementedException(); // Don't need to support this + + public IChangeToken Watch(string filter) + => throw new NotImplementedException(); // Don't need to support this + } +} diff --git a/runtime/Microsoft.Blazor.Server/BlazorAppBuilderExtensions.cs b/runtime/Microsoft.Blazor.Server/BlazorAppBuilderExtensions.cs new file mode 100644 index 0000000000..9c23044bcf --- /dev/null +++ b/runtime/Microsoft.Blazor.Server/BlazorAppBuilderExtensions.cs @@ -0,0 +1,33 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.StaticFiles; +using Microsoft.Blazor.Mono; +using System.Collections.Generic; +using System.Net.Mime; + +namespace Microsoft.AspNetCore.Builder +{ + public static class BlazorAppBuilderExtensions + { + public static void UseBlazor(this IApplicationBuilder applicationBuilder) + { + applicationBuilder.UseStaticFiles(new StaticFileOptions + { + RequestPath = "/_framework", + FileProvider = new MonoStaticFileProvider(), + ContentTypeProvider = CreateContentTypeProvider(), + }); + } + + private static IContentTypeProvider CreateContentTypeProvider() + { + return new FileExtensionContentTypeProvider(new Dictionary + { + { ".dll", MediaTypeNames.Application.Octet }, + { ".js", "application/javascript" }, + { ".wasm", MediaTypeNames.Application.Octet } + }); + } + } +} diff --git a/runtime/Microsoft.Blazor.Server/Microsoft.Blazor.Server.csproj b/runtime/Microsoft.Blazor.Server/Microsoft.Blazor.Server.csproj new file mode 100644 index 0000000000..f749977536 --- /dev/null +++ b/runtime/Microsoft.Blazor.Server/Microsoft.Blazor.Server.csproj @@ -0,0 +1,15 @@ + + + + netcoreapp2.0 + + + + + + + + + + + diff --git a/samples/MonoSanity/MonoSanity.csproj b/samples/MonoSanity/MonoSanity.csproj index d7c2e491fc..c800d91356 100644 --- a/samples/MonoSanity/MonoSanity.csproj +++ b/samples/MonoSanity/MonoSanity.csproj @@ -8,4 +8,8 @@ + + + + diff --git a/samples/MonoSanity/Startup.cs b/samples/MonoSanity/Startup.cs index 286fb20173..b478e25de8 100644 --- a/samples/MonoSanity/Startup.cs +++ b/samples/MonoSanity/Startup.cs @@ -12,6 +12,7 @@ namespace MonoSanity { app.UseDeveloperExceptionPage(); app.UseFileServer(); + app.UseBlazor(); } } } diff --git a/test/Microsoft.Blazor.Mono.Test/Microsoft.Blazor.Mono.Test.csproj b/test/Microsoft.Blazor.Mono.Test/Microsoft.Blazor.Mono.Test.csproj new file mode 100644 index 0000000000..3fb42b363b --- /dev/null +++ b/test/Microsoft.Blazor.Mono.Test/Microsoft.Blazor.Mono.Test.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp2.0 + + false + + + + + + + + + + + + + + diff --git a/test/Microsoft.Blazor.Mono.Test/MonoStaticFileProviderTest.cs b/test/Microsoft.Blazor.Mono.Test/MonoStaticFileProviderTest.cs new file mode 100644 index 0000000000..40631e6350 --- /dev/null +++ b/test/Microsoft.Blazor.Mono.Test/MonoStaticFileProviderTest.cs @@ -0,0 +1,58 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Xunit; + +namespace Microsoft.Blazor.Mono.Test +{ + public class MonoStaticFileProviderTest + { + [Fact] + public void SuppliesMonoFiles() + { + var provider = new MonoStaticFileProvider(); + + // This is not an exhaustive list. The set of BCL facade types is long and + // will probably change. This test is just to verify the resource embedding + // and filename mapping is working correctly. + var expectedFiles = new[] + { + "/asmjs/mono.asm.js", + "/wasm/mono.wasm", + "/bcl/mscorlib.dll", + "/bcl/Facades/System.Collections.dll", + }; + + foreach (var name in expectedFiles) + { + var fileInfo = provider.GetFileInfo(name); + Assert.True(fileInfo.Exists); + Assert.False(fileInfo.IsDirectory); + Assert.True(fileInfo.Length > 0); + } + } + + [Fact] + public void DoesNotSupplyUnexpectedFiles() + { + var provider = new MonoStaticFileProvider(); + + var notExpectedFiles = new[] + { + "", + "mono", + "wasm", + "/wasm", + "/wasm/", + "wasm/mono.wasm", + "/wasm/../wasm/mono.wasm", + }; + + foreach (var name in notExpectedFiles) + { + var fileInfo = provider.GetFileInfo(name); + Assert.False(fileInfo.Exists); + } + } + } +}