Webpack HMR EventSource requests are now proxied (rather than redirected) to the local HMR server. Fixes #271.

This commit is contained in:
SteveSandersonMS
2016-09-09 16:31:15 +01:00
parent bc2de2ad59
commit f071590fce
3 changed files with 22 additions and 21 deletions

View File

@@ -30,6 +30,7 @@ namespace Microsoft.AspNetCore.SpaServices.Webpack
_pathPrefix = pathPrefix; _pathPrefix = pathPrefix;
_options = options; _options = options;
_httpClient = new HttpClient(new HttpClientHandler()); _httpClient = new HttpClient(new HttpClientHandler());
_httpClient.Timeout = _options.RequestTimeout;
} }
public async Task Invoke(HttpContext context) public async Task Invoke(HttpContext context)

View File

@@ -1,16 +1,20 @@
using System;
namespace Microsoft.AspNetCore.SpaServices.Webpack namespace Microsoft.AspNetCore.SpaServices.Webpack
{ {
internal class ConditionalProxyMiddlewareOptions internal class ConditionalProxyMiddlewareOptions
{ {
public ConditionalProxyMiddlewareOptions(string scheme, string host, string port) public ConditionalProxyMiddlewareOptions(string scheme, string host, string port, TimeSpan requestTimeout)
{ {
Scheme = scheme; Scheme = scheme;
Host = host; Host = host;
Port = port; Port = port;
RequestTimeout = requestTimeout;
} }
public string Scheme { get; } public string Scheme { get; }
public string Host { get; } public string Host { get; }
public string Port { get; } public string Port { get; }
public TimeSpan RequestTimeout { get; }
} }
} }

View File

@@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.PlatformAbstractions; using Microsoft.Extensions.PlatformAbstractions;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Threading;
// Putting in this namespace so it's always available whenever MapRoute is // Putting in this namespace so it's always available whenever MapRoute is
@@ -14,8 +15,6 @@ namespace Microsoft.AspNetCore.Builder
{ {
public static class WebpackDevMiddleware public static class WebpackDevMiddleware
{ {
private const string WebpackDevMiddlewareScheme = "http";
private const string WebpackHotMiddlewareEndpoint = "/__webpack_hmr";
private const string DefaultConfigFile = "webpack.config.js"; private const string DefaultConfigFile = "webpack.config.js";
public static void UseWebpackDevMiddleware( public static void UseWebpackDevMiddleware(
@@ -62,30 +61,27 @@ namespace Microsoft.AspNetCore.Builder
JsonConvert.SerializeObject(devServerOptions)).Result; JsonConvert.SerializeObject(devServerOptions)).Result;
// Proxy the corresponding requests through ASP.NET and into the Node listener // Proxy the corresponding requests through ASP.NET and into the Node listener
// Anything under /<publicpath> (e.g., /dist) is proxied as a normal HTTP request with a typical timeout (100s is the default from HttpClient),
// plus /__webpack_hmr is proxied with infinite timeout, because it's an EventSource (long-lived request).
appBuilder.UseProxyToLocalWebpackDevMiddleware(devServerInfo.PublicPath, devServerInfo.Port, TimeSpan.FromSeconds(100));
appBuilder.UseProxyToLocalWebpackDevMiddleware("/__webpack_hmr", devServerInfo.Port, Timeout.InfiniteTimeSpan);
}
private static void UseProxyToLocalWebpackDevMiddleware(this IApplicationBuilder appBuilder, string publicPath, int proxyToPort, TimeSpan requestTimeout)
{
// Note that this is hardcoded to make requests to "localhost" regardless of the hostname of the // Note that this is hardcoded to make requests to "localhost" regardless of the hostname of the
// server as far as the client is concerned. This is because ConditionalProxyMiddlewareOptions is // server as far as the client is concerned. This is because ConditionalProxyMiddlewareOptions is
// the one making the internal HTTP requests, and it's going to be to some port on this machine // the one making the internal HTTP requests, and it's going to be to some port on this machine
// because aspnet-webpack hosts the dev server there. We can't use the hostname that the client // because aspnet-webpack hosts the dev server there. We can't use the hostname that the client
// sees, because that could be anything (e.g., some upstream load balancer) and we might not be // sees, because that could be anything (e.g., some upstream load balancer) and we might not be
// able to make outbound requests to it from here. // able to make outbound requests to it from here.
var proxyOptions = new ConditionalProxyMiddlewareOptions(WebpackDevMiddlewareScheme, // Also note that the webpack HMR service always uses HTTP, even if your app server uses HTTPS,
"localhost", devServerInfo.Port.ToString()); // because the HMR service has no need for HTTPS (the client doesn't see it directly - all traffic
appBuilder.UseMiddleware<ConditionalProxyMiddleware>(devServerInfo.PublicPath, proxyOptions); // to it is proxied), and the HMR service couldn't use HTTPS anyway (in general it wouldn't have
// the necessary certificate).
// While it would be nice to proxy the /__webpack_hmr requests too, these return an EventStream, var proxyOptions = new ConditionalProxyMiddlewareOptions(
// and the Microsoft.AspNetCore.Proxy code doesn't handle that entirely - it throws an exception after "http", "localhost", proxyToPort.ToString(), requestTimeout);
// a while. So, just serve a 302 for those. But note that we must use the hostname that the client appBuilder.UseMiddleware<ConditionalProxyMiddleware>(publicPath, proxyOptions);
// sees, not "localhost", so that it works even when you're not running on localhost (e.g., Docker).
appBuilder.Map(WebpackHotMiddlewareEndpoint, builder =>
{
builder.Use(next => ctx =>
{
var hostname = ctx.Request.Host.Host;
ctx.Response.Redirect(
$"{WebpackDevMiddlewareScheme}://{hostname}:{devServerInfo.Port.ToString()}{WebpackHotMiddlewareEndpoint}");
return Task.FromResult(0);
});
});
} }
#pragma warning disable CS0649 #pragma warning disable CS0649