|
7 | 7 | using Microsoft.AspNetCore.Hosting; |
8 | 8 | using Microsoft.Extensions.PlatformAbstractions; |
9 | 9 | using Newtonsoft.Json; |
10 | | -using System.Threading; |
11 | 10 |
|
12 | 11 | // Putting in this namespace so it's always available whenever MapRoute is |
13 | 12 |
|
14 | 13 | namespace Microsoft.AspNetCore.Builder |
15 | 14 | { |
16 | 15 | public static class WebpackDevMiddleware |
17 | 16 | { |
| 17 | + private const string WebpackDevMiddlewareScheme = "http"; |
| 18 | + private const string WebpackHotMiddlewareEndpoint = "/__webpack_hmr"; |
18 | 19 | private const string DefaultConfigFile = "webpack.config.js"; |
19 | 20 |
|
20 | 21 | public static void UseWebpackDevMiddleware( |
@@ -61,27 +62,30 @@ public static void UseWebpackDevMiddleware( |
61 | 62 | JsonConvert.SerializeObject(devServerOptions)).Result; |
62 | 63 |
|
63 | 64 | // 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 | | - { |
72 | 65 | // Note that this is hardcoded to make requests to "localhost" regardless of the hostname of the |
73 | 66 | // server as far as the client is concerned. This is because ConditionalProxyMiddlewareOptions is |
74 | 67 | // the one making the internal HTTP requests, and it's going to be to some port on this machine |
75 | 68 | // because aspnet-webpack hosts the dev server there. We can't use the hostname that the client |
76 | 69 | // sees, because that could be anything (e.g., some upstream load balancer) and we might not be |
77 | 70 | // able to make outbound requests to it from here. |
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); |
| 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 | + }); |
85 | 89 | } |
86 | 90 |
|
87 | 91 | #pragma warning disable CS0649 |
|
0 commit comments