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);
+ }
+ }
+ }
+}