From a1921de0be3fe54b208ee55152733aa1da9d2ab6 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Thu, 1 Mar 2018 10:32:58 +0000 Subject: [PATCH 01/26] Initial rough implementation, hardcoded always to be on, and at a fixed URL --- .../src/Boot.ts | 5 + .../src/LiveReloading.ts | 47 +++++++ .../BlazorAppBuilderExtensions.cs | 10 +- .../BlazorConfig.cs | 2 + .../LiveReloading.cs | 118 ++++++++++++++++++ 5 files changed, 177 insertions(+), 5 deletions(-) create mode 100644 src/Microsoft.AspNetCore.Blazor.Browser.JS/src/LiveReloading.ts create mode 100644 src/Microsoft.AspNetCore.Blazor.Server/LiveReloading.cs diff --git a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Boot.ts b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Boot.ts index 065c71152..a51afb88d 100644 --- a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Boot.ts +++ b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Boot.ts @@ -1,5 +1,6 @@ import { platform } from './Environment'; import { getAssemblyNameFromUrl } from './Platform/DotNet'; +import { enableLiveReloading } from './LiveReloading'; import './Rendering/Renderer'; import './Services/Http'; import './Services/UriHelper'; @@ -36,6 +37,10 @@ async function boot() { // Start up the application platform.callEntryPoint(entryPointAssemblyName, entryPointMethod, []); + + // TODO: Get the live reloading endpoint from the + + + diff --git a/test/testapps/LiveReloadTestApp/wwwroot/someJsFile.js b/test/testapps/LiveReloadTestApp/wwwroot/someJsFile.js new file mode 100644 index 000000000..af61769b2 --- /dev/null +++ b/test/testapps/LiveReloadTestApp/wwwroot/someJsFile.js @@ -0,0 +1,3 @@ +// We modify this on disk during E2E tests to verify it causes a reload +var valueToWrite = 'initial value'; +document.getElementById('some-js-file-output').textContent = valueToWrite; From f3951146cea2f7b19942a675c5588590170b4402 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Thu, 1 Mar 2018 13:18:55 +0000 Subject: [PATCH 05/26] E2E fixes --- .../Infrastructure/BrowserFixture.cs | 2 +- test/testapps/LiveReloadTestApp/wwwroot/index.html | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/test/Microsoft.AspNetCore.Blazor.E2ETest/Infrastructure/BrowserFixture.cs b/test/Microsoft.AspNetCore.Blazor.E2ETest/Infrastructure/BrowserFixture.cs index 5980e4d67..3cdd7ef79 100644 --- a/test/Microsoft.AspNetCore.Blazor.E2ETest/Infrastructure/BrowserFixture.cs +++ b/test/Microsoft.AspNetCore.Blazor.E2ETest/Infrastructure/BrowserFixture.cs @@ -15,7 +15,7 @@ public class BrowserFixture : IDisposable public BrowserFixture() { var opts = new ChromeOptions(); - //opts.AddArgument("--headless"); + opts.AddArgument("--headless"); // On Windows/Linux, we don't need to set opts.BinaryLocation // But for Travis Mac builds we do diff --git a/test/testapps/LiveReloadTestApp/wwwroot/index.html b/test/testapps/LiveReloadTestApp/wwwroot/index.html index bc57ad933..5c7d816ec 100644 --- a/test/testapps/LiveReloadTestApp/wwwroot/index.html +++ b/test/testapps/LiveReloadTestApp/wwwroot/index.html @@ -8,7 +8,12 @@ Loading...
- + + From c55c19af4902f59d24ce2d69889df91ef626687a Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Thu, 1 Mar 2018 13:56:43 +0000 Subject: [PATCH 06/26] Keep reload signals within the context of an individual UseBlazorLiveReloading call --- ...veReloading.cs => LiveReloadingContext.cs} | 37 +++++++++---------- .../LiveReloadingExtensions.cs | 21 +++++++++++ .../Tests/LiveReloadingTest.cs | 1 + 3 files changed, 39 insertions(+), 20 deletions(-) rename src/Microsoft.AspNetCore.Blazor.Server/{LiveReloading.cs => LiveReloadingContext.cs} (79%) create mode 100644 src/Microsoft.AspNetCore.Blazor.Server/LiveReloadingExtensions.cs diff --git a/src/Microsoft.AspNetCore.Blazor.Server/LiveReloading.cs b/src/Microsoft.AspNetCore.Blazor.Server/LiveReloadingContext.cs similarity index 79% rename from src/Microsoft.AspNetCore.Blazor.Server/LiveReloading.cs rename to src/Microsoft.AspNetCore.Blazor.Server/LiveReloadingContext.cs index f03a50037..926fea5aa 100644 --- a/src/Microsoft.AspNetCore.Blazor.Server/LiveReloading.cs +++ b/src/Microsoft.AspNetCore.Blazor.Server/LiveReloadingContext.cs @@ -11,39 +11,36 @@ namespace Microsoft.AspNetCore.Blazor.Server { - internal static class LiveReloading + internal class LiveReloadingContext { // Keep in sync with the const in Microsoft.AspNetCore.Blazor.Build's AppBuilder.cs - const string BlazorBuildCompletedSignalFile = "__blazorBuildCompleted"; + private const string BlazorBuildCompletedSignalFile = "__blazorBuildCompleted"; // If some external automated process is writing multiple files to wwwroot, // you probably want to wait until they've all been written before reloading. // Pausing by 500 milliseconds is a crude effort - we might need a different // mechanism (e.g., waiting until writes have stopped by 500ms). - const int WebRootUpdateDelayMilliseconds = 500; + private const int WebRootUpdateDelayMilliseconds = 500; // TODO: REMOVE THIS + private const string heartbeatMessage = "data: alive\n\n"; + private const string reloadMessage = "data: reload\n\n"; - // If we don't hold references to them, then on Linux they get disposed + // If we don't hold references to them, then on Linux they get disposed. // TODO: Review if this is still true - readonly static List _pinnedWatchers = new List(); + // This static would leak memory if you called UseBlazorLiveReloading continually + // throughout the app lifetime, but the intended usage is just during init. + private static readonly List _pinnedWatchers = new List(); - readonly static string heartbeatMessage = $"data: alive\n\n"; - readonly static string reloadMessage = $"data: reload\n\n"; - readonly static object _currentReloadListenerLock = new object(); - static CancellationTokenSource _currentReloadListener + private readonly object _currentReloadListenerLock = new object(); + private CancellationTokenSource _currentReloadListener = new CancellationTokenSource(); - public static void UseBlazorLiveReloading( - this IApplicationBuilder applicationBuilder, - BlazorConfig config) + public void Attach(IApplicationBuilder applicationBuilder, BlazorConfig config) { - if (!string.IsNullOrEmpty(config.ReloadUri)) - { - CreateFileSystemWatchers(config); - AddEventStreamEndpoint(applicationBuilder, config.ReloadUri); - } + CreateFileSystemWatchers(config); + AddEventStreamEndpoint(applicationBuilder, config.ReloadUri); } - private static void AddEventStreamEndpoint(IApplicationBuilder applicationBuilder, string url) + private void AddEventStreamEndpoint(IApplicationBuilder applicationBuilder, string url) { applicationBuilder.Use(async (context, next) => { @@ -80,7 +77,7 @@ private static void AddEventStreamEndpoint(IApplicationBuilder applicationBuilde }); } - private static void CreateFileSystemWatchers(BlazorConfig config) + private void CreateFileSystemWatchers(BlazorConfig config) { // Watch for the "build completed" signal in the dist dir var distFileWatcher = new FileSystemWatcher(config.DistPath); @@ -109,7 +106,7 @@ private static void CreateFileSystemWatchers(BlazorConfig config) } } - private static void RequestReload(int delayMilliseconds) + private void RequestReload(int delayMilliseconds) { Task.Delay(delayMilliseconds).ContinueWith(_ => { diff --git a/src/Microsoft.AspNetCore.Blazor.Server/LiveReloadingExtensions.cs b/src/Microsoft.AspNetCore.Blazor.Server/LiveReloadingExtensions.cs new file mode 100644 index 000000000..1ebb85996 --- /dev/null +++ b/src/Microsoft.AspNetCore.Blazor.Server/LiveReloadingExtensions.cs @@ -0,0 +1,21 @@ +// 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.Builder; + +namespace Microsoft.AspNetCore.Blazor.Server +{ + internal static class LiveReloadingExtensions + { + public static void UseBlazorLiveReloading( + this IApplicationBuilder applicationBuilder, + BlazorConfig config) + { + if (!string.IsNullOrEmpty(config.ReloadUri)) + { + var context = new LiveReloadingContext(); + context.Attach(applicationBuilder, config); + } + } + } +} diff --git a/test/Microsoft.AspNetCore.Blazor.E2ETest/Tests/LiveReloadingTest.cs b/test/Microsoft.AspNetCore.Blazor.E2ETest/Tests/LiveReloadingTest.cs index b6b14bb02..e1b5da8a5 100644 --- a/test/Microsoft.AspNetCore.Blazor.E2ETest/Tests/LiveReloadingTest.cs +++ b/test/Microsoft.AspNetCore.Blazor.E2ETest/Tests/LiveReloadingTest.cs @@ -50,6 +50,7 @@ public void ReloadsWhenWebRootFilesAreModified() // See that the page reloads and reflects the updated source file new WebDriverWait(Browser, TimeSpan.FromSeconds(30)).Until( driver => driver.FindElement(jsFileOutputSelector).Text == "modified value"); + WaitUntilLoaded(); } finally { From c818cd9df1e42bc8c98a580971b7210b2edbdd0e Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Thu, 1 Mar 2018 14:06:10 +0000 Subject: [PATCH 07/26] Remove unnecessary reload delay --- .../LiveReloadingContext.cs | 33 ++++++++----------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/src/Microsoft.AspNetCore.Blazor.Server/LiveReloadingContext.cs b/src/Microsoft.AspNetCore.Blazor.Server/LiveReloadingContext.cs index 926fea5aa..9280eb8ff 100644 --- a/src/Microsoft.AspNetCore.Blazor.Server/LiveReloadingContext.cs +++ b/src/Microsoft.AspNetCore.Blazor.Server/LiveReloadingContext.cs @@ -16,11 +16,7 @@ internal class LiveReloadingContext // Keep in sync with the const in Microsoft.AspNetCore.Blazor.Build's AppBuilder.cs private const string BlazorBuildCompletedSignalFile = "__blazorBuildCompleted"; - // If some external automated process is writing multiple files to wwwroot, - // you probably want to wait until they've all been written before reloading. - // Pausing by 500 milliseconds is a crude effort - we might need a different - // mechanism (e.g., waiting until writes have stopped by 500ms). - private const int WebRootUpdateDelayMilliseconds = 500; // TODO: REMOVE THIS + // These strings are in the format needed by EventSource private const string heartbeatMessage = "data: alive\n\n"; private const string reloadMessage = "data: reload\n\n"; @@ -84,7 +80,7 @@ private void CreateFileSystemWatchers(BlazorConfig config) distFileWatcher.Deleted += (sender, eventArgs) => { if (eventArgs.Name.Equals(BlazorBuildCompletedSignalFile, StringComparison.Ordinal)) { - RequestReload(0); + RequestReload(); } }; distFileWatcher.EnableRaisingEvents = true; @@ -97,27 +93,24 @@ private void CreateFileSystemWatchers(BlazorConfig config) if (!string.IsNullOrEmpty(config.WebRootPath)) { var webRootWatcher = new FileSystemWatcher(config.WebRootPath); - webRootWatcher.Deleted += (sender, evtArgs) => RequestReload(WebRootUpdateDelayMilliseconds); - webRootWatcher.Created += (sender, evtArgs) => RequestReload(WebRootUpdateDelayMilliseconds); - webRootWatcher.Changed += (sender, evtArgs) => RequestReload(WebRootUpdateDelayMilliseconds); - webRootWatcher.Renamed += (sender, evtArgs) => RequestReload(WebRootUpdateDelayMilliseconds); + webRootWatcher.Deleted += (sender, evtArgs) => RequestReload(); + webRootWatcher.Created += (sender, evtArgs) => RequestReload(); + webRootWatcher.Changed += (sender, evtArgs) => RequestReload(); + webRootWatcher.Renamed += (sender, evtArgs) => RequestReload(); webRootWatcher.EnableRaisingEvents = true; _pinnedWatchers.Add(webRootWatcher); } } - private void RequestReload(int delayMilliseconds) + private void RequestReload() { - Task.Delay(delayMilliseconds).ContinueWith(_ => + lock (_currentReloadListenerLock) { - lock (_currentReloadListenerLock) - { - // Lock just to be sure two threads don't assign different new CTSs, of which - // only one would later get cancelled. - _currentReloadListener.Cancel(); - _currentReloadListener = new CancellationTokenSource(); - } - }); + // Lock just to be sure two threads don't assign different new CTSs, of which + // only one would later get cancelled. + _currentReloadListener.Cancel(); + _currentReloadListener = new CancellationTokenSource(); + } } } } From df12cfb7415f20c165e281c5bdfb9d57f1e61f5d Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Thu, 1 Mar 2018 14:18:40 +0000 Subject: [PATCH 08/26] Revert "Remove unnecessary reload delay" This reverts commit 5b45014050811b63f1fa913f587c70d90150c340. --- .../LiveReloadingContext.cs | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/Microsoft.AspNetCore.Blazor.Server/LiveReloadingContext.cs b/src/Microsoft.AspNetCore.Blazor.Server/LiveReloadingContext.cs index 9280eb8ff..f3ae7c6c5 100644 --- a/src/Microsoft.AspNetCore.Blazor.Server/LiveReloadingContext.cs +++ b/src/Microsoft.AspNetCore.Blazor.Server/LiveReloadingContext.cs @@ -16,7 +16,11 @@ internal class LiveReloadingContext // Keep in sync with the const in Microsoft.AspNetCore.Blazor.Build's AppBuilder.cs private const string BlazorBuildCompletedSignalFile = "__blazorBuildCompleted"; - // These strings are in the format needed by EventSource + // If some external automated process is writing multiple files to wwwroot, + // you probably want to wait until they've all been written before reloading. + // Pausing by 500 milliseconds is a crude effort - we might need a different + // mechanism (e.g., waiting until writes have stopped by 500ms). + private const int WebRootUpdateDelayMilliseconds = 500; private const string heartbeatMessage = "data: alive\n\n"; private const string reloadMessage = "data: reload\n\n"; @@ -80,7 +84,7 @@ private void CreateFileSystemWatchers(BlazorConfig config) distFileWatcher.Deleted += (sender, eventArgs) => { if (eventArgs.Name.Equals(BlazorBuildCompletedSignalFile, StringComparison.Ordinal)) { - RequestReload(); + RequestReload(0); } }; distFileWatcher.EnableRaisingEvents = true; @@ -93,24 +97,27 @@ private void CreateFileSystemWatchers(BlazorConfig config) if (!string.IsNullOrEmpty(config.WebRootPath)) { var webRootWatcher = new FileSystemWatcher(config.WebRootPath); - webRootWatcher.Deleted += (sender, evtArgs) => RequestReload(); - webRootWatcher.Created += (sender, evtArgs) => RequestReload(); - webRootWatcher.Changed += (sender, evtArgs) => RequestReload(); - webRootWatcher.Renamed += (sender, evtArgs) => RequestReload(); + webRootWatcher.Deleted += (sender, evtArgs) => RequestReload(WebRootUpdateDelayMilliseconds); + webRootWatcher.Created += (sender, evtArgs) => RequestReload(WebRootUpdateDelayMilliseconds); + webRootWatcher.Changed += (sender, evtArgs) => RequestReload(WebRootUpdateDelayMilliseconds); + webRootWatcher.Renamed += (sender, evtArgs) => RequestReload(WebRootUpdateDelayMilliseconds); webRootWatcher.EnableRaisingEvents = true; _pinnedWatchers.Add(webRootWatcher); } } - private void RequestReload() + private void RequestReload(int delayMilliseconds) { - lock (_currentReloadListenerLock) + Task.Delay(delayMilliseconds).ContinueWith(_ => { - // Lock just to be sure two threads don't assign different new CTSs, of which - // only one would later get cancelled. - _currentReloadListener.Cancel(); - _currentReloadListener = new CancellationTokenSource(); - } + lock (_currentReloadListenerLock) + { + // Lock just to be sure two threads don't assign different new CTSs, of which + // only one would later get cancelled. + _currentReloadListener.Cancel(); + _currentReloadListener = new CancellationTokenSource(); + } + }); } } } From 096aecb9a3469b50a4577216055ca6113aec9a1a Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Thu, 1 Mar 2018 18:44:56 +0000 Subject: [PATCH 09/26] Avoid spurious error message in Firefox --- .../src/LiveReloading.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/LiveReloading.ts b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/LiveReloading.ts index dd76cbef3..4f7c42458 100644 --- a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/LiveReloading.ts +++ b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/LiveReloading.ts @@ -44,6 +44,11 @@ function listenForReloadEvent(eventSourceUrl: string, reloadOnConnection: boolea } } }); + + // Needed for some versions of Firefox + window.addEventListener('beforeunload', () => { + source.close(); + }); } function resolveAgainstBaseUri(uri: string) { From e791265f9148e2f1d3dfee0d5cb9b730bdbdd7d4 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 16 Mar 2018 12:06:40 +0000 Subject: [PATCH 10/26] Post-rebase fixes --- Blazor.sln | 6 +++- .../Cli/Commands/BuildIndexHtmlCommand.cs | 5 ++++ .../targets/Blazor.MonoRuntime.targets | 2 +- .../IndexHtmlWriterTest.cs | 28 +++++++++++++++++-- 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/Blazor.sln b/Blazor.sln index e7d593ca2..853eabe2a 100644 --- a/Blazor.sln +++ b/Blazor.sln @@ -283,6 +283,7 @@ Global {F3E02B21-1127-431A-B832-0E53CB72097B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F3E02B21-1127-431A-B832-0E53CB72097B}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU {F3E02B21-1127-431A-B832-0E53CB72097B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F3E02B21-1127-431A-B832-0E53CB72097B}.Release|Any CPU.Build.0 = Release|Any CPU {F3E02B21-1127-431A-B832-0E53CB72097B}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU {FF25111E-5A3E-48A3-96D8-08A2C5A2A91C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FF25111E-5A3E-48A3-96D8-08A2C5A2A91C}.Debug|Any CPU.Build.0 = Debug|Any CPU @@ -314,11 +315,14 @@ Global {C57382BC-EE93-49D5-BC40-5C98AF8AA048}.Release|Any CPU.Build.0 = Release|Any CPU {C57382BC-EE93-49D5-BC40-5C98AF8AA048}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU {C57382BC-EE93-49D5-BC40-5C98AF8AA048}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU - {F3E02B21-1127-431A-B832-0E53CB72097B}.Release|Any CPU.Build.0 = Release|Any CPU {0246AA77-1A27-4A67-874B-6EF6F99E414E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0246AA77-1A27-4A67-874B-6EF6F99E414E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0246AA77-1A27-4A67-874B-6EF6F99E414E}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU + {0246AA77-1A27-4A67-874B-6EF6F99E414E}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU {0246AA77-1A27-4A67-874B-6EF6F99E414E}.Release|Any CPU.ActiveCfg = Release|Any CPU {0246AA77-1A27-4A67-874B-6EF6F99E414E}.Release|Any CPU.Build.0 = Release|Any CPU + {0246AA77-1A27-4A67-874B-6EF6F99E414E}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU + {0246AA77-1A27-4A67-874B-6EF6F99E414E}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Microsoft.AspNetCore.Blazor.Build/Cli/Commands/BuildIndexHtmlCommand.cs b/src/Microsoft.AspNetCore.Blazor.Build/Cli/Commands/BuildIndexHtmlCommand.cs index 5f160d64f..f3a3dd19a 100644 --- a/src/Microsoft.AspNetCore.Blazor.Build/Cli/Commands/BuildIndexHtmlCommand.cs +++ b/src/Microsoft.AspNetCore.Blazor.Build/Cli/Commands/BuildIndexHtmlCommand.cs @@ -26,6 +26,10 @@ public static void Command(CommandLineApplication command) "Adds a tag with the specified 'href' value", CommandOptionType.MultipleValue); + var reloadUri = command.Option("--reload-uri", + "If specified, enables live reloading and specifies the URI of the notification endpoint.", + CommandOptionType.SingleValue); + var outputPath = command.Option("--output", "Path to the output file", CommandOptionType.SingleValue); @@ -55,6 +59,7 @@ public static void Command(CommandLineApplication command) jsReferences.Values.ToArray(), cssReferences.Values.ToArray(), linkerEnabledFlag.HasValue(), + reloadUri.Value(), outputPath.Value()); return 0; } diff --git a/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.targets b/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.targets index 7e89c059d..bd70b6eca 100644 --- a/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.targets +++ b/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.targets @@ -595,10 +595,10 @@ <_AppReferences Include="@(BlazorItemOutput->WithMetadataValue('Type','Assembly')->WithMetadataValue('PrimaryOutput','')->'%(FileName)%(Extension)')" /> <_JsReferences Include="@(BlazorPackageJsRef->'_content/%(SourcePackage)/%(RecursiveDir)%(FileName)%(Extension)')" /> <_CssReferences Include="@(BlazorPackageCssRef->'_content/%(SourcePackage)/%(RecursiveDir)%(FileName)%(Extension)')" /> - <_LiveReloadArg Condition="'$(LiveReloadUri)' != ''">--reload-uri "$(LiveReloadUri)" <_LinkerEnabledFlag Condition="'$(_BlazorShouldLinkApplicationAssemblies)' != ''">--linker-enabled + <_LiveReloadArg Condition="'$(LiveReloadUri)' != ''">--reload-uri "$(LiveReloadUri)" diff --git a/test/Microsoft.AspNetCore.Blazor.Build.Test/IndexHtmlWriterTest.cs b/test/Microsoft.AspNetCore.Blazor.Build.Test/IndexHtmlWriterTest.cs index 6b5bdae3a..f3cd2ed66 100644 --- a/test/Microsoft.AspNetCore.Blazor.Build.Test/IndexHtmlWriterTest.cs +++ b/test/Microsoft.AspNetCore.Blazor.Build.Test/IndexHtmlWriterTest.cs @@ -36,7 +36,8 @@ Some text assemblyReferences, jsReferences, cssReferences, - linkerEnabled: true); + linkerEnabled: true, + reloadUri: "/my/reload"); // Act & Assert: Start and end is not modified (including formatting) Assert.StartsWith(htmlTemplatePrefix, instance); @@ -68,6 +69,29 @@ Some text linkElems.Select(tag => tag.GetAttribute("href")), cssReferences); } + + [Fact] + public void OmitsReloadAttributeIfNoReloadUriSpecified() + { + // Arrange + var htmlTemplate = ""; + var assemblyReferences = new string[] { "System.Abc.dll", "MyApp.ClassLib.dll", }; + + // Act + var fileContents = IndexHtmlWriter.GetIndexHtmlContents( + htmlTemplate, + "MyApp.Entrypoint", + "MyNamespace.MyType::MyMethod", + assemblyReferences, + /* js references */ new string[] {}, + /* js references */ new string[] {}, + /* reloadUri */ null); + + // Assert + var parsedHtml = new HtmlParser().Parse(fileContents); + var scriptElem = parsedHtml.QuerySelector("script"); + Assert.False(scriptElem.HasAttribute("reload")); + } [Fact] public void SuppliesHtmlTemplateUnchangedIfNoBootScriptPresent() @@ -79,7 +103,7 @@ public void SuppliesHtmlTemplateUnchangedIfNoBootScriptPresent() var cssReferences = new string[] { "my/styles.css" }; var content = IndexHtmlWriter.GetIndexHtmlContents( - htmlTemplate, "MyApp.Entrypoint", "MyNamespace.MyType::MyMethod", assemblyReferences, jsReferences, cssReferences, linkerEnabled: true); + htmlTemplate, "MyApp.Entrypoint", "MyNamespace.MyType::MyMethod", assemblyReferences, jsReferences, cssReferences, linkerEnabled: true, reloadUri: "/my/reload"); // Assert Assert.Equal(htmlTemplate, content); From a508168d8935f29ffc673fb0eaa02cd203cfda30 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 16 Mar 2018 12:28:06 +0000 Subject: [PATCH 11/26] Update build-completed signal to match new build mechanism --- .../targets/Blazor.MonoRuntime.props | 6 ++++-- .../targets/Blazor.MonoRuntime.targets | 4 ++++ .../LiveReloadingContext.cs | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.props b/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.props index caa2852a3..324847430 100644 --- a/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.props +++ b/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.props @@ -11,8 +11,9 @@ -c link -u link -t --verbose - dist/_content/ - dist/_framework/ + dist/ + $(BaseBlazorDistPath)_content/ + $(BaseBlazorDistPath)_framework/ $(BaseBlazorRuntimeOutputPath)_bin/ $(BaseBlazorRuntimeOutputPath)asmjs/ $(BaseBlazorRuntimeOutputPath)wasm/ @@ -21,6 +22,7 @@ wwwroot/ Index.html $(BlazorIndexHtmlName.ToLowerInvariant()) + $(BaseBlazorDistPath)__blazorBuildCompleted diff --git a/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.targets b/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.targets index bd70b6eca..80b4f982b 100644 --- a/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.targets +++ b/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.targets @@ -19,6 +19,10 @@ UseSymboliclinksIfPossible="$(CreateSymbolicLinksForCopyFilesToOutputDirectoryIfPossible)" Condition="'@(BlazorItemOutput)' != '' and '$(CopyBuildOutputToOutputDirectory)' == 'true' and '$(SkipCopyBuildProduct)' != 'true'"> + + + + diff --git a/src/Microsoft.AspNetCore.Blazor.Server/LiveReloadingContext.cs b/src/Microsoft.AspNetCore.Blazor.Server/LiveReloadingContext.cs index f3ae7c6c5..5329b9007 100644 --- a/src/Microsoft.AspNetCore.Blazor.Server/LiveReloadingContext.cs +++ b/src/Microsoft.AspNetCore.Blazor.Server/LiveReloadingContext.cs @@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Blazor.Server { internal class LiveReloadingContext { - // Keep in sync with the const in Microsoft.AspNetCore.Blazor.Build's AppBuilder.cs + // Keep in sync with $(BlazorBuildCompletedSignalPath) in Blazor.MonoRuntime.props private const string BlazorBuildCompletedSignalFile = "__blazorBuildCompleted"; // If some external automated process is writing multiple files to wwwroot, From cbf44cc77a46f93c5e6e1910cdfd00cc2029cdba Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 16 Mar 2018 12:32:55 +0000 Subject: [PATCH 12/26] Add missing assertion that was lost during rebase --- .../IndexHtmlWriterTest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Microsoft.AspNetCore.Blazor.Build.Test/IndexHtmlWriterTest.cs b/test/Microsoft.AspNetCore.Blazor.Build.Test/IndexHtmlWriterTest.cs index f3cd2ed66..08f3a68c0 100644 --- a/test/Microsoft.AspNetCore.Blazor.Build.Test/IndexHtmlWriterTest.cs +++ b/test/Microsoft.AspNetCore.Blazor.Build.Test/IndexHtmlWriterTest.cs @@ -54,6 +54,7 @@ Some text Assert.Equal("MyApp.Entrypoint.dll", scriptElem.GetAttribute("main")); Assert.Equal("MyNamespace.MyType::MyMethod", scriptElem.GetAttribute("entrypoint")); Assert.Equal("System.Abc.dll,MyApp.ClassLib.dll", scriptElem.GetAttribute("references")); + Assert.Equal("/my/reload", scriptElem.GetAttribute("reload")); Assert.False(scriptElem.HasAttribute("type")); Assert.Equal(string.Empty, scriptElem.Attributes["custom1"].Value); Assert.Equal("value", scriptElem.Attributes["custom2"].Value); From a1d325814ceae8f6b77cd791a32f50509fb06312 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 16 Mar 2018 13:08:23 +0000 Subject: [PATCH 13/26] Fix live reloading E2E test following build mechanism changes --- .../Tests/LiveReloadingTest.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/test/Microsoft.AspNetCore.Blazor.E2ETest/Tests/LiveReloadingTest.cs b/test/Microsoft.AspNetCore.Blazor.E2ETest/Tests/LiveReloadingTest.cs index e1b5da8a5..725cd8c92 100644 --- a/test/Microsoft.AspNetCore.Blazor.E2ETest/Tests/LiveReloadingTest.cs +++ b/test/Microsoft.AspNetCore.Blazor.E2ETest/Tests/LiveReloadingTest.cs @@ -75,10 +75,11 @@ public void ReloadsWhenBlazorAppRebuilds() File.WriteAllText(cshtmlFilePath, newContents); // Trigger build + var buildConfiguration = DetectBuildConfiguration(_serverFixture.ContentRoot); var buildProcess = Process.Start(new ProcessStartInfo { FileName = "dotnet", - Arguments = "build --no-restore --no-dependencies", + Arguments = $"build --no-restore --no-dependencies -c {buildConfiguration}", WorkingDirectory = _serverFixture.ContentRoot }); Assert.True(buildProcess.WaitForExit(30 * 1000)); @@ -95,6 +96,18 @@ public void ReloadsWhenBlazorAppRebuilds() } } + private object DetectBuildConfiguration(string contentRoot) + { + // We want the test to issue the build with the same configuration that + // the project was already built with (otherwise there will be errors because + // of having multiple directories under /bin, plus it means we don't need + // to restore and rebuild all dependencies so it's faster) + var binDirInfo = new DirectoryInfo(Path.Combine(contentRoot, "bin")); + var configurationDirs = binDirInfo.GetDirectories(); + Assert.Single(configurationDirs); + return configurationDirs[0].Name; + } + private void WaitUntilLoaded() { new WebDriverWait(Browser, TimeSpan.FromSeconds(30)).Until( From a3def66f92194f2c13a3dd00a006119030b5d1ff Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 16 Mar 2018 13:37:06 +0000 Subject: [PATCH 14/26] Make live reloading easier to turn on/off --- src/Microsoft.AspNetCore.Blazor.Build/targets/All.props | 4 ++++ src/Microsoft.AspNetCore.Blazor.Build/targets/All.targets | 6 +----- .../targets/Blazor.MonoRuntime.targets | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.AspNetCore.Blazor.Build/targets/All.props b/src/Microsoft.AspNetCore.Blazor.Build/targets/All.props index 919d5cba0..f8bc950f0 100644 --- a/src/Microsoft.AspNetCore.Blazor.Build/targets/All.props +++ b/src/Microsoft.AspNetCore.Blazor.Build/targets/All.props @@ -4,6 +4,10 @@ $(DefaultWebContentItemExcludes);wwwroot\** + + true + /reload + true diff --git a/src/Microsoft.AspNetCore.Blazor.Build/targets/All.targets b/src/Microsoft.AspNetCore.Blazor.Build/targets/All.targets index 6ff71d46f..f5c862d6f 100644 --- a/src/Microsoft.AspNetCore.Blazor.Build/targets/All.targets +++ b/src/Microsoft.AspNetCore.Blazor.Build/targets/All.targets @@ -8,10 +8,6 @@ dotnet "$(MSBuildThisFileDirectory)../tools/Microsoft.AspNetCore.Blazor.Build.dll" - - - /_reload - true @@ -27,7 +23,7 @@ - + diff --git a/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.targets b/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.targets index 80b4f982b..df7e0298a 100644 --- a/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.targets +++ b/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.targets @@ -575,7 +575,7 @@ - + <_LinkerEnabledFlag Condition="'$(_BlazorShouldLinkApplicationAssemblies)' != ''">--linker-enabled - <_LiveReloadArg Condition="'$(LiveReloadUri)' != ''">--reload-uri "$(LiveReloadUri)" + <_LiveReloadArg Condition="'$(UseBlazorLiveReloading)'=='true' AND '$(BlazorLiveReloadUri)'!=''">--reload-uri "$(BlazorLiveReloadUri)" From 37ab3cd655e6b0e7b1f8de70a79f64eb0a9b92ec Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 16 Mar 2018 13:38:11 +0000 Subject: [PATCH 15/26] Enable live reloading on test app when running in CI --- test/testapps/LiveReloadTestApp/LiveReloadTestApp.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/testapps/LiveReloadTestApp/LiveReloadTestApp.csproj b/test/testapps/LiveReloadTestApp/LiveReloadTestApp.csproj index bb0dcc234..dd97d39d7 100644 --- a/test/testapps/LiveReloadTestApp/LiveReloadTestApp.csproj +++ b/test/testapps/LiveReloadTestApp/LiveReloadTestApp.csproj @@ -6,6 +6,9 @@ dotnet run --project ..\..\..\src\Microsoft.AspNetCore.Blazor.Cli serve --pathbase /live/reloading/subdir + + + true From d38e085d7049c66f27ce91a3d41e853d77539547 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 16 Mar 2018 13:38:52 +0000 Subject: [PATCH 16/26] Make reload URI less likely to clash with an application path --- src/Microsoft.AspNetCore.Blazor.Build/targets/All.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.AspNetCore.Blazor.Build/targets/All.props b/src/Microsoft.AspNetCore.Blazor.Build/targets/All.props index f8bc950f0..0ea3d840f 100644 --- a/src/Microsoft.AspNetCore.Blazor.Build/targets/All.props +++ b/src/Microsoft.AspNetCore.Blazor.Build/targets/All.props @@ -6,7 +6,7 @@ true - /reload + /_reload true From 0984189fbd47d34b1592d5dcff0aec3f13f91f7c Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 16 Mar 2018 14:06:23 +0000 Subject: [PATCH 17/26] Don't issue the reload notification until the build is really finished --- .../targets/Blazor.MonoRuntime.targets | 16 ++++++++++++---- .../LiveReloadingContext.cs | 4 +++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.targets b/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.targets index df7e0298a..8ebe05868 100644 --- a/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.targets +++ b/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.targets @@ -20,13 +20,12 @@ Condition="'@(BlazorItemOutput)' != '' and '$(CopyBuildOutputToOutputDirectory)' == 'true' and '$(SkipCopyBuildProduct)' != 'true'"> - - - - + + <_BlazorDidCopyFilesToOutputDirectory>true + @@ -51,6 +50,14 @@ + + + + + + true /_reload - + true + + + + + From d00b9aec8b97d3d33a70b5cc309feaa381ae94be Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 30 Mar 2018 15:07:51 +0100 Subject: [PATCH 23/26] Don't issue reload notifications when building inside VS. This needs to be done from the VS extension instead. --- .../targets/Blazor.MonoRuntime.targets | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.targets b/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.targets index 8332b863a..66003529d 100644 --- a/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.targets +++ b/src/Microsoft.AspNetCore.Blazor.Build/targets/Blazor.MonoRuntime.targets @@ -50,9 +50,16 @@ + + Condition="'$(UseBlazorLiveReloading)'=='true' AND '$(_BlazorDidCopyFilesToOutputDirectory)'=='true' AND '$(BuildingInsideVisualStudio)'!='true'"> From 8c876d6960592359288ef28bab9558d4f35d90fe Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 30 Mar 2018 16:06:36 +0100 Subject: [PATCH 24/26] Switch from EventSource to WebSocket for live reloading notifications --- .../src/LiveReloading.ts | 45 ++++------- .../LiveReloadingContext.cs | 80 +++++++++++-------- .../Microsoft.AspNetCore.Blazor.Server.csproj | 1 + 3 files changed, 63 insertions(+), 63 deletions(-) diff --git a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/LiveReloading.ts b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/LiveReloading.ts index 76cc01e6a..422026eb2 100644 --- a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/LiveReloading.ts +++ b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/LiveReloading.ts @@ -1,51 +1,34 @@ const reconnectionPollIntervalMs = 250; -export function enableLiveReloading(eventSourceUrl: string) { - listenForReloadEvent(eventSourceUrl); +export function enableLiveReloading(endpointUri: string) { + listenForReloadEvent(endpointUri); } -function listenForReloadEvent(eventSourceUrl: string) { - const EventSource = window['EventSource']; - if (!EventSource) { - console.log('Browser does not support EventSource, so live reloading will be disabled.'); +function listenForReloadEvent(endpointUri: string) { + if (!WebSocket) { + console.log('Browser does not support WebSocket, so live reloading will be disabled.'); return; } // First, connect to the endpoint - const source = new EventSource(resolveAgainstBaseUri(eventSourceUrl)); - let sourceDidOpen; - source.addEventListener('open', e => { - sourceDidOpen = true; - }); + const source = new WebSocket(toAbsoluteWebSocketUri(endpointUri)); - // If we're notified that we should reload, then do so. - source.addEventListener('message', e => { + // If we're notified that we should reload, then do so + source.onmessage = e => { if (e.data === 'reload') { location.reload(); } - }); - - // If the server disconnects (e.g., because the app is being recycled), then - // stop listening. - source.addEventListener('error', e => { - if (source.readyState === 0 && sourceDidOpen) { - source.close(); - } - }); - - // Needed for some versions of Firefox - window.addEventListener('beforeunload', () => { - source.close(); - }); + }; } -function resolveAgainstBaseUri(uri: string) { +function toAbsoluteWebSocketUri(uri: string) { const baseUri = document.baseURI; if (baseUri) { const lastSlashPos = baseUri.lastIndexOf('/'); const prefix = baseUri.substr(0, lastSlashPos); - return prefix + uri; - } else { - return uri; + uri = prefix + uri; } + + // Scheme must be ws: or wss: + return uri.replace(/^http/, 'ws'); } diff --git a/src/Microsoft.AspNetCore.Blazor.Server/LiveReloadingContext.cs b/src/Microsoft.AspNetCore.Blazor.Server/LiveReloadingContext.cs index ec5580778..67ae6074b 100644 --- a/src/Microsoft.AspNetCore.Blazor.Server/LiveReloadingContext.cs +++ b/src/Microsoft.AspNetCore.Blazor.Server/LiveReloadingContext.cs @@ -6,6 +6,8 @@ using System; using System.Collections.Generic; using System.IO; +using System.Net.WebSockets; +using System.Text; using System.Threading; using System.Threading.Tasks; @@ -21,8 +23,7 @@ internal class LiveReloadingContext // Pausing by 500 milliseconds is a crude effort - we might need a different // mechanism (e.g., waiting until writes have stopped by 500ms). private const int WebRootUpdateDelayMilliseconds = 500; - private const string heartbeatMessage = "data: alive\n\n"; - private const string reloadMessage = "data: reload\n\n"; + private static byte[] _reloadMessage = Encoding.UTF8.GetBytes("reload"); // If we don't hold references to them, then on Linux they get disposed. // This static would leak memory if you called UseBlazorLiveReloading continually @@ -36,48 +37,63 @@ private CancellationTokenSource _currentReloadListener public void Attach(IApplicationBuilder applicationBuilder, BlazorConfig config) { CreateFileSystemWatchers(config); - AddEventStreamEndpoint(applicationBuilder, config.ReloadUri); + AddWebSocketsEndpoint(applicationBuilder, config.ReloadUri); } - private void AddEventStreamEndpoint(IApplicationBuilder applicationBuilder, string url) + private void AddWebSocketsEndpoint(IApplicationBuilder applicationBuilder, string url) { - applicationBuilder.Use(async (context, next) => + applicationBuilder.UseWebSockets(); + applicationBuilder.Use((context, next) => { if (!string.Equals(context.Request.Path, url, StringComparison.Ordinal)) { - await next(); + return next(); } - else + + if (!context.WebSockets.IsWebSocketRequest) { - context.Response.ContentType = "text/event-stream"; - var reloadToken = _currentReloadListener.Token; - var reloadOrRequestAbortedToken = CancellationTokenSource - .CreateLinkedTokenSource(reloadToken, context.RequestAborted) - .Token; - while (!context.RequestAborted.IsCancellationRequested) - { - await context.Response.WriteAsync(heartbeatMessage); - await context.Response.WriteAsync(Environment.NewLine); - await context.Response.Body.FlushAsync(); - try - { - await Task.Delay(5000, reloadOrRequestAbortedToken); - } - catch (TaskCanceledException) - { - if (reloadToken.IsCancellationRequested) - { - await context.Response.WriteAsync(reloadMessage); - await context.Response.WriteAsync(Environment.NewLine); - await context.Response.Body.FlushAsync(); - } - break; - } - } + context.Response.StatusCode = 400; + return context.Response.WriteAsync("This endpoint only accepts WebSockets connections."); } + + return HandleWebSocketRequest( + context.WebSockets.AcceptWebSocketAsync(), + context.RequestAborted); }); } + private async Task HandleWebSocketRequest(Task webSocketTask, CancellationToken requestAbortedToken) + { + var webSocket = await webSocketTask; + var reloadToken = _currentReloadListener.Token; + + // Wait until either we get a signal to trigger a reload, or the client disconnects + // In either case we're done after that. It's the client's job to reload and start + // a new live reloading context. + try + { + var reloadOrRequestAbortedToken = CancellationTokenSource + .CreateLinkedTokenSource(reloadToken, requestAbortedToken) + .Token; + await Task.Delay(-1, reloadOrRequestAbortedToken); + } + catch (TaskCanceledException) + { + if (reloadToken.IsCancellationRequested) + { + await webSocket.SendAsync( + _reloadMessage, + WebSocketMessageType.Text, + true, + requestAbortedToken); + await webSocket.CloseAsync( + WebSocketCloseStatus.NormalClosure, + "Requested reload", + requestAbortedToken); + } + } + } + private void CreateFileSystemWatchers(BlazorConfig config) { // Watch for the "build completed" signal in the dist dir diff --git a/src/Microsoft.AspNetCore.Blazor.Server/Microsoft.AspNetCore.Blazor.Server.csproj b/src/Microsoft.AspNetCore.Blazor.Server/Microsoft.AspNetCore.Blazor.Server.csproj index fdab225fe..b06b3d525 100644 --- a/src/Microsoft.AspNetCore.Blazor.Server/Microsoft.AspNetCore.Blazor.Server.csproj +++ b/src/Microsoft.AspNetCore.Blazor.Server/Microsoft.AspNetCore.Blazor.Server.csproj @@ -7,6 +7,7 @@ + From f18e3962ce92f67bd79bd1784d8c58cae7deea0f Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Mon, 2 Apr 2018 14:49:22 +0100 Subject: [PATCH 25/26] On the server, ensure live reloading is never enabled in the Production environment, regardless of client app config --- .../src/LiveReloading.ts | 18 +++++++++++++++++- .../BlazorAppBuilderExtensions.cs | 11 ++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/LiveReloading.ts b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/LiveReloading.ts index 422026eb2..7d6426f66 100644 --- a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/LiveReloading.ts +++ b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/LiveReloading.ts @@ -11,7 +11,23 @@ function listenForReloadEvent(endpointUri: string) { } // First, connect to the endpoint - const source = new WebSocket(toAbsoluteWebSocketUri(endpointUri)); + const websocketUri = toAbsoluteWebSocketUri(endpointUri); + const source = new WebSocket(websocketUri); + let allowConnectionFailedErrorReporting = true; + + source.onopen = e => { + allowConnectionFailedErrorReporting = false; + }; + + source.onerror = e => { + if (allowConnectionFailedErrorReporting) { + allowConnectionFailedErrorReporting = false; + console.error(`The client app was compiled with live reloading enabled, but could not open ` + + ` a WebSocket connection to the server at ${websocketUri}\n` + + `To fix this inconsistency, either run the server in development mode, or compile the ` + + `client app in Release configuration.`); + } + }; // If we're notified that we should reload, then do so source.onmessage = e => { diff --git a/src/Microsoft.AspNetCore.Blazor.Server/BlazorAppBuilderExtensions.cs b/src/Microsoft.AspNetCore.Blazor.Server/BlazorAppBuilderExtensions.cs index 390fbe78f..0f77a7e1c 100644 --- a/src/Microsoft.AspNetCore.Blazor.Server/BlazorAppBuilderExtensions.cs +++ b/src/Microsoft.AspNetCore.Blazor.Server/BlazorAppBuilderExtensions.cs @@ -64,9 +64,14 @@ public static void UseBlazor( }); } - // Whether or not live reloading is actually enabled depends on the config - // For production builds, it won't be (by default) - applicationBuilder.UseBlazorLiveReloading(config); + // Definitely don't open a listener for live reloading in production, even if the + // client app was compiled with live reloading enabled + if (env.IsDevelopment()) + { + // Whether or not live reloading is actually enabled depends on the client config + // For release builds, it won't be (by default) + applicationBuilder.UseBlazorLiveReloading(config); + } // Finally, use SPA fallback routing (serve default page for anything else, // excluding /_framework/*) From 7893896ccd98d9f24acf77764578447a432dd1d1 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Mon, 2 Apr 2018 15:10:41 +0100 Subject: [PATCH 26/26] Always run Live Reloading E2E tests in Development environment --- .../ServerFixtures/DevHostServerFixture.cs | 14 ++++++++++++-- .../Tests/LiveReloadingTest.cs | 1 + 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/test/Microsoft.AspNetCore.Blazor.E2ETest/Infrastructure/ServerFixtures/DevHostServerFixture.cs b/test/Microsoft.AspNetCore.Blazor.E2ETest/Infrastructure/ServerFixtures/DevHostServerFixture.cs index 7d77e3b2e..08825ecfb 100644 --- a/test/Microsoft.AspNetCore.Blazor.E2ETest/Infrastructure/ServerFixtures/DevHostServerFixture.cs +++ b/test/Microsoft.AspNetCore.Blazor.E2ETest/Infrastructure/ServerFixtures/DevHostServerFixture.cs @@ -2,12 +2,14 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.AspNetCore.Hosting; +using System.Collections.Generic; using DevHostServerProgram = Microsoft.AspNetCore.Blazor.Cli.Server.Program; namespace Microsoft.AspNetCore.Blazor.E2ETest.Infrastructure.ServerFixtures { public class DevHostServerFixture : WebHostServerFixture { + public string Environment { get; set; } public string PathBase { get; set; } public string ContentRoot { get; private set; } @@ -16,12 +18,20 @@ protected override IWebHost CreateWebHost() ContentRoot = FindSampleOrTestSitePath( typeof(TProgram).Assembly.GetName().Name); - return DevHostServerProgram.BuildWebHost(new string[] + var args = new List { "--urls", "http://127.0.0.1:0", "--contentroot", ContentRoot, "--pathbase", PathBase - }); + }; + + if (!string.IsNullOrEmpty(Environment)) + { + args.Add("--environment"); + args.Add(Environment); + } + + return DevHostServerProgram.BuildWebHost(args.ToArray()); } } } diff --git a/test/Microsoft.AspNetCore.Blazor.E2ETest/Tests/LiveReloadingTest.cs b/test/Microsoft.AspNetCore.Blazor.E2ETest/Tests/LiveReloadingTest.cs index 7e711248e..925fdfd33 100644 --- a/test/Microsoft.AspNetCore.Blazor.E2ETest/Tests/LiveReloadingTest.cs +++ b/test/Microsoft.AspNetCore.Blazor.E2ETest/Tests/LiveReloadingTest.cs @@ -27,6 +27,7 @@ public LiveReloadingTest(BrowserFixture browserFixture, DevHostServerFixture