diff --git a/src/Microsoft.AspNetCore.SpaServices/Webpack/ConditionalProxyMiddleware.cs b/src/Microsoft.AspNetCore.SpaServices/Webpack/ConditionalProxyMiddleware.cs index 5a56c81..72b66c3 100644 --- a/src/Microsoft.AspNetCore.SpaServices/Webpack/ConditionalProxyMiddleware.cs +++ b/src/Microsoft.AspNetCore.SpaServices/Webpack/ConditionalProxyMiddleware.cs @@ -30,7 +30,6 @@ namespace Microsoft.AspNetCore.SpaServices.Webpack _pathPrefix = pathPrefix; _options = options; _httpClient = new HttpClient(new HttpClientHandler()); - _httpClient.Timeout = _options.RequestTimeout; } public async Task Invoke(HttpContext context) diff --git a/src/Microsoft.AspNetCore.SpaServices/Webpack/ConditionalProxyMiddlewareOptions.cs b/src/Microsoft.AspNetCore.SpaServices/Webpack/ConditionalProxyMiddlewareOptions.cs index 2c3311a..5654007 100644 --- a/src/Microsoft.AspNetCore.SpaServices/Webpack/ConditionalProxyMiddlewareOptions.cs +++ b/src/Microsoft.AspNetCore.SpaServices/Webpack/ConditionalProxyMiddlewareOptions.cs @@ -1,20 +1,16 @@ -using System; - namespace Microsoft.AspNetCore.SpaServices.Webpack { internal class ConditionalProxyMiddlewareOptions { - public ConditionalProxyMiddlewareOptions(string scheme, string host, string port, TimeSpan requestTimeout) + public ConditionalProxyMiddlewareOptions(string scheme, string host, string port) { Scheme = scheme; Host = host; Port = port; - RequestTimeout = requestTimeout; } public string Scheme { get; } public string Host { get; } public string Port { get; } - public TimeSpan RequestTimeout { get; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.SpaServices/Webpack/WebpackDevMiddleware.cs b/src/Microsoft.AspNetCore.SpaServices/Webpack/WebpackDevMiddleware.cs index d046c28..dcc5d4f 100644 --- a/src/Microsoft.AspNetCore.SpaServices/Webpack/WebpackDevMiddleware.cs +++ b/src/Microsoft.AspNetCore.SpaServices/Webpack/WebpackDevMiddleware.cs @@ -7,7 +7,6 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.PlatformAbstractions; using Newtonsoft.Json; -using System.Threading; // Putting in this namespace so it's always available whenever MapRoute is @@ -15,6 +14,8 @@ namespace Microsoft.AspNetCore.Builder { public static class WebpackDevMiddleware { + private const string WebpackDevMiddlewareScheme = "http"; + private const string WebpackHotMiddlewareEndpoint = "/__webpack_hmr"; private const string DefaultConfigFile = "webpack.config.js"; public static void UseWebpackDevMiddleware( @@ -61,27 +62,30 @@ namespace Microsoft.AspNetCore.Builder JsonConvert.SerializeObject(devServerOptions)).Result; // Proxy the corresponding requests through ASP.NET and into the Node listener - // Anything under / (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 // 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 // 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 // able to make outbound requests to it from here. - // Also note that the webpack HMR service always uses HTTP, even if your app server uses HTTPS, - // because the HMR service has no need for HTTPS (the client doesn't see it directly - all traffic - // to it is proxied), and the HMR service couldn't use HTTPS anyway (in general it wouldn't have - // the necessary certificate). - var proxyOptions = new ConditionalProxyMiddlewareOptions( - "http", "localhost", proxyToPort.ToString(), requestTimeout); - appBuilder.UseMiddleware(publicPath, proxyOptions); + var proxyOptions = new ConditionalProxyMiddlewareOptions(WebpackDevMiddlewareScheme, + "localhost", devServerInfo.Port.ToString()); + appBuilder.UseMiddleware(devServerInfo.PublicPath, proxyOptions); + + // While it would be nice to proxy the /__webpack_hmr requests too, these return an EventStream, + // and the Microsoft.AspNetCore.Proxy code doesn't handle that entirely - it throws an exception after + // a while. So, just serve a 302 for those. But note that we must use the hostname that the client + // 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