From f1283940b773113b503f7929a044ce4d03cab9aa Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Wed, 4 Apr 2018 14:06:30 +0100 Subject: [PATCH] In live reloading, also reload if the server is being recycled. Plus rewrite LiveReloading.ts completely to be better organized. --- .../src/LiveReloading.ts | 100 +++++++++++++----- 1 file changed, 72 insertions(+), 28 deletions(-) diff --git a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/LiveReloading.ts b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/LiveReloading.ts index b0fd3e4702..bff0583523 100644 --- a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/LiveReloading.ts +++ b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/LiveReloading.ts @@ -1,38 +1,78 @@ -export function enableLiveReloading(endpointUri: string) { - listenForReloadEvent(endpointUri); +const pollIntervalMs = 500; +const maxPollDurationMs = 10 * 1000; + +export function enableLiveReloading(endpointUri: string) { + new ReloadContext(endpointUri).start(); } -function listenForReloadEvent(endpointUri: string) { - if (!WebSocket) { - console.log('Browser does not support WebSocket, so live reloading will be disabled.'); - return; +class ReloadContext { + private _websocketUri: string; + private _didConnect = false; + private _pollUntilConnectedThenReload = false; + private _stopPollingAtTime: Date | null = null; + + constructor(endpointUri: string) { + this._websocketUri = toAbsoluteWebSocketUri(endpointUri); } - // First, connect to the endpoint - 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.`); + start() { + if (typeof WebSocket !== 'undefined') { + this._attemptToConnect(/* delay */ 0); + } else { + console.log('Browser does not support WebSocket, so live reloading will be disabled.'); } - }; + } - // If we're notified that we should reload, then do so - source.onmessage = e => { - if (e.data === 'reload') { - location.reload(); + private _attemptToConnect(delayMs: number) { + setTimeout(() => { + const source = new WebSocket(this._websocketUri); + source.onopen = event => this._onOpen(); + source.onmessage = event => this._onMessage(event.data); + source.onerror = event => this._onError(); + source.onclose = event => this._onClose(); + }, delayMs); + } + + private _onOpen() { + this._didConnect = true; + + if (this._pollUntilConnectedThenReload) { + reloadNow(); } - }; + } + + private _onMessage(data: string) { + if (data === 'reload') { + reloadNow(); + } + } + + private _onClose() { + if (this._didConnect) { + // Looks like the server is being recycled (or possibly just shut down, but in the + // absence of a graceful shutdown we have no way to tell the difference) + // Wait until the server appears to be back, then reload + this._pollUntilConnectedThenReload = true; + this._stopPollingAtTime = new Date(new Date().valueOf() + maxPollDurationMs); + this._attemptToConnect(/* delay */ pollIntervalMs); + } + } + + private _onError() { + if (!this._didConnect) { + if (this._pollUntilConnectedThenReload) { + if (new Date() < this._stopPollingAtTime!) { + // Continue polling + this._attemptToConnect(/* delay */ pollIntervalMs); + } + } else { + console.error(`The client app was compiled with live reloading enabled, but could not open ` + + ` a WebSocket connection to the server at ${this._websocketUri}\n` + + `To fix this inconsistency, either run the server in development mode, or compile the ` + + `client app in Release configuration.`); + } + } + } } function toAbsoluteWebSocketUri(uri: string) { @@ -46,3 +86,7 @@ function toAbsoluteWebSocketUri(uri: string) { // Scheme must be ws: or wss: return uri.replace(/^http/, 'ws'); } + +function reloadNow() { + location.reload(); +} \ No newline at end of file