Skip to content

Commit f071590

Browse files
Webpack HMR EventSource requests are now proxied (rather than redirected) to the local HMR server. Fixes aspnet#271.
1 parent bc2de2a commit f071590

File tree

3 files changed

+22
-21
lines changed

3 files changed

+22
-21
lines changed

src/Microsoft.AspNetCore.SpaServices/Webpack/ConditionalProxyMiddleware.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public ConditionalProxyMiddleware(
3030
_pathPrefix = pathPrefix;
3131
_options = options;
3232
_httpClient = new HttpClient(new HttpClientHandler());
33+
_httpClient.Timeout = _options.RequestTimeout;
3334
}
3435

3536
public async Task Invoke(HttpContext context)
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
1+
using System;
2+
13
namespace Microsoft.AspNetCore.SpaServices.Webpack
24
{
35
internal class ConditionalProxyMiddlewareOptions
46
{
5-
public ConditionalProxyMiddlewareOptions(string scheme, string host, string port)
7+
public ConditionalProxyMiddlewareOptions(string scheme, string host, string port, TimeSpan requestTimeout)
68
{
79
Scheme = scheme;
810
Host = host;
911
Port = port;
12+
RequestTimeout = requestTimeout;
1013
}
1114

1215
public string Scheme { get; }
1316
public string Host { get; }
1417
public string Port { get; }
18+
public TimeSpan RequestTimeout { get; }
1519
}
1620
}

src/Microsoft.AspNetCore.SpaServices/Webpack/WebpackDevMiddleware.cs

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,14 @@
77
using Microsoft.AspNetCore.Hosting;
88
using Microsoft.Extensions.PlatformAbstractions;
99
using Newtonsoft.Json;
10+
using System.Threading;
1011

1112
// Putting in this namespace so it's always available whenever MapRoute is
1213

1314
namespace Microsoft.AspNetCore.Builder
1415
{
1516
public static class WebpackDevMiddleware
1617
{
17-
private const string WebpackDevMiddlewareScheme = "http";
18-
private const string WebpackHotMiddlewareEndpoint = "/__webpack_hmr";
1918
private const string DefaultConfigFile = "webpack.config.js";
2019

2120
public static void UseWebpackDevMiddleware(
@@ -62,30 +61,27 @@ public static void UseWebpackDevMiddleware(
6261
JsonConvert.SerializeObject(devServerOptions)).Result;
6362

6463
// Proxy the corresponding requests through ASP.NET and into the Node listener
64+
// Anything under /<publicpath> (e.g., /dist) is proxied as a normal HTTP request with a typical timeout (100s is the default from HttpClient),
65+
// plus /__webpack_hmr is proxied with infinite timeout, because it's an EventSource (long-lived request).
66+
appBuilder.UseProxyToLocalWebpackDevMiddleware(devServerInfo.PublicPath, devServerInfo.Port, TimeSpan.FromSeconds(100));
67+
appBuilder.UseProxyToLocalWebpackDevMiddleware("/__webpack_hmr", devServerInfo.Port, Timeout.InfiniteTimeSpan);
68+
}
69+
70+
private static void UseProxyToLocalWebpackDevMiddleware(this IApplicationBuilder appBuilder, string publicPath, int proxyToPort, TimeSpan requestTimeout)
71+
{
6572
// Note that this is hardcoded to make requests to "localhost" regardless of the hostname of the
6673
// server as far as the client is concerned. This is because ConditionalProxyMiddlewareOptions is
6774
// the one making the internal HTTP requests, and it's going to be to some port on this machine
6875
// because aspnet-webpack hosts the dev server there. We can't use the hostname that the client
6976
// sees, because that could be anything (e.g., some upstream load balancer) and we might not be
7077
// able to make outbound requests to it from here.
71-
var proxyOptions = new ConditionalProxyMiddlewareOptions(WebpackDevMiddlewareScheme,
72-
"localhost", devServerInfo.Port.ToString());
73-
appBuilder.UseMiddleware<ConditionalProxyMiddleware>(devServerInfo.PublicPath, proxyOptions);
74-
75-
// While it would be nice to proxy the /__webpack_hmr requests too, these return an EventStream,
76-
// and the Microsoft.AspNetCore.Proxy code doesn't handle that entirely - it throws an exception after
77-
// a while. So, just serve a 302 for those. But note that we must use the hostname that the client
78-
// sees, not "localhost", so that it works even when you're not running on localhost (e.g., Docker).
79-
appBuilder.Map(WebpackHotMiddlewareEndpoint, builder =>
80-
{
81-
builder.Use(next => ctx =>
82-
{
83-
var hostname = ctx.Request.Host.Host;
84-
ctx.Response.Redirect(
85-
$"{WebpackDevMiddlewareScheme}://{hostname}:{devServerInfo.Port.ToString()}{WebpackHotMiddlewareEndpoint}");
86-
return Task.FromResult(0);
87-
});
88-
});
78+
// Also note that the webpack HMR service always uses HTTP, even if your app server uses HTTPS,
79+
// because the HMR service has no need for HTTPS (the client doesn't see it directly - all traffic
80+
// to it is proxied), and the HMR service couldn't use HTTPS anyway (in general it wouldn't have
81+
// the necessary certificate).
82+
var proxyOptions = new ConditionalProxyMiddlewareOptions(
83+
"http", "localhost", proxyToPort.ToString(), requestTimeout);
84+
appBuilder.UseMiddleware<ConditionalProxyMiddleware>(publicPath, proxyOptions);
8985
}
9086

9187
#pragma warning disable CS0649

0 commit comments

Comments
 (0)