Commit c0fd5696 authored by Buns Shar's avatar Buns Shar Committed by Joe Haddad
Browse files

Resolve localhost when offline on Windows (#1839)

* Change proxy localhost to I27.0.0.1 for windows

* Update comment

* resolve localhost IP with DNS lookup on windows

* Fix CI errors

* Promisify addWebpackMiddleware

* Remove Node 6 syntax

* Update addWebpackMiddleware.js

* Actually use the resolved proxy
parent 31a38b34
Showing with 104 additions and 62 deletions
+104 -62
...@@ -80,22 +80,30 @@ function run(port) { ...@@ -80,22 +80,30 @@ function run(port) {
const devServer = new WebpackDevServer(compiler, devServerConfig); const devServer = new WebpackDevServer(compiler, devServerConfig);
// Our custom middleware proxies requests to /index.html or a remote API. // Our custom middleware proxies requests to /index.html or a remote API.
addWebpackMiddleware(devServer); addWebpackMiddleware(devServer)
.then(() => {
// Launch WebpackDevServer. // Launch WebpackDevServer.
devServer.listen(port, host, err => { devServer.listen(port, host, err => {
if (err) { if (err) {
return console.log(err); return console.log(err);
} }
if (isInteractive) { if (isInteractive) {
clearConsole(); clearConsole();
} }
console.log(chalk.cyan('Starting the development server...')); console.log(chalk.cyan('Starting the development server...'));
console.log(); console.log();
openBrowser(`${protocol}://${host}:${port}/`); openBrowser(`${protocol}://${host}:${port}/`);
}); });
})
.catch(e => {
console.log(
chalk.red('Failed to setup middleware, please report this error:')
);
console.log(e);
process.exit(1);
});
} }
// We attempt to use the default port but if it is busy, we offer the user to // We attempt to use the default port but if it is busy, we offer the user to
......
...@@ -11,8 +11,10 @@ ...@@ -11,8 +11,10 @@
'use strict'; 'use strict';
const chalk = require('chalk'); const chalk = require('chalk');
const dns = require('dns');
const historyApiFallback = require('connect-history-api-fallback'); const historyApiFallback = require('connect-history-api-fallback');
const httpProxyMiddleware = require('http-proxy-middleware'); const httpProxyMiddleware = require('http-proxy-middleware');
const url = require('url');
const paths = require('../../config/paths'); const paths = require('../../config/paths');
// We need to provide a custom onError function for httpProxyMiddleware. // We need to provide a custom onError function for httpProxyMiddleware.
...@@ -56,49 +58,57 @@ function onProxyError(proxy) { ...@@ -56,49 +58,57 @@ function onProxyError(proxy) {
}; };
} }
module.exports = function addWebpackMiddleware(devServer) { function resolveProxy(proxy) {
// `proxy` lets you to specify a fallback server during development. const p = url.parse(proxy);
// Every unrecognized request will be forwarded to it. const hostname = p.hostname;
const proxy = require(paths.appPackageJson).proxy; if (hostname !== 'localhost') {
devServer.use( return Promise.resolve(proxy);
historyApiFallback({ }
// Paths with dots should still use the history fallback. p.host = undefined; // Remove the host; we don't care about it
// See https://github.com/facebookincubator/create-react-app/issues/387. return new Promise(resolve => {
disableDotRule: true, dns.lookup(hostname, { hints: 0, all: false }, (err, address) => {
// For single page apps, we generally want to fallback to /index.html. if (err) {
// However we also want to respect `proxy` for API calls. console.log(
// So if `proxy` is specified, we need to decide which fallback to use. chalk.red(
// We use a heuristic: if request `accept`s text/html, we pick /index.html. '"proxy" in package.json is set to localhost and cannot be resolved.'
// Modern browsers include text/html into `accept` header when navigating. )
// However API calls like `fetch()` won’t generally accept text/html. );
// If this heuristic doesn’t work well for you, don’t use `proxy`. console.log(
htmlAcceptHeaders: proxy ? ['text/html'] : ['text/html', '*/*'], chalk.red('Try setting "proxy" to 127.0.0.1 instead of localhost.')
}) );
); process.exit(1);
if (proxy) { }
if (typeof proxy !== 'string') { p.hostname = address;
console.log( resolve(url.format(p));
chalk.red('When specified, "proxy" in package.json must be a string.') });
); });
console.log( }
chalk.red('Instead, the type of "proxy" was "' + typeof proxy + '".')
); function registerProxy(devServer, _proxy) {
console.log( if (typeof _proxy !== 'string') {
chalk.red( console.log(
'Either remove "proxy" from package.json, or make it a string.' chalk.red('When specified, "proxy" in package.json must be a string.')
) );
); console.log(
process.exit(1); chalk.red('Instead, the type of "proxy" was "' + typeof _proxy + '".')
// Test that proxy url specified starts with http:// or https:// );
} else if (!/^http(s)?:\/\//.test(proxy)) { console.log(
console.log( chalk.red('Either remove "proxy" from package.json, or make it a string.')
chalk.red( );
'When "proxy" is specified in package.json it must start with either http:// or https://' process.exit(1);
) // Test that proxy url specified starts with http:// or https://
); } else if (!/^http(s)?:\/\//.test(_proxy)) {
process.exit(1); console.log(
} chalk.red(
'When "proxy" is specified in package.json it must start with either http:// or https://'
)
);
process.exit(1);
}
return (process.platform === 'win32'
? resolveProxy(_proxy)
: Promise.resolve(_proxy)).then(proxy => {
// Otherwise, if proxy is specified, we will let it handle any request. // Otherwise, if proxy is specified, we will let it handle any request.
// There are a few exceptions which we won't send to the proxy: // There are a few exceptions which we won't send to the proxy:
// - /index.html (served as HTML5 history API fallback) // - /index.html (served as HTML5 history API fallback)
...@@ -132,9 +142,33 @@ module.exports = function addWebpackMiddleware(devServer) { ...@@ -132,9 +142,33 @@ module.exports = function addWebpackMiddleware(devServer) {
// If this is not done, httpProxyMiddleware will not try to upgrade until // If this is not done, httpProxyMiddleware will not try to upgrade until
// an initial plain HTTP request is made. // an initial plain HTTP request is made.
devServer.listeningApp.on('upgrade', hpm.upgrade); devServer.listeningApp.on('upgrade', hpm.upgrade);
} });
}
// Finally, by now we have certainly resolved the URL. module.exports = function addWebpackMiddleware(devServer) {
// It may be /index.html, so let the dev server try serving it again. // `proxy` lets you to specify a fallback server during development.
devServer.use(devServer.middleware); // Every unrecognized request will be forwarded to it.
const proxy = require(paths.appPackageJson).proxy;
devServer.use(
historyApiFallback({
// Paths with dots should still use the history fallback.
// See https://github.com/facebookincubator/create-react-app/issues/387.
disableDotRule: true,
// For single page apps, we generally want to fallback to /index.html.
// However we also want to respect `proxy` for API calls.
// So if `proxy` is specified, we need to decide which fallback to use.
// We use a heuristic: if request `accept`s text/html, we pick /index.html.
// Modern browsers include text/html into `accept` header when navigating.
// However API calls like `fetch()` won’t generally accept text/html.
// If this heuristic doesn’t work well for you, don’t use `proxy`.
htmlAcceptHeaders: proxy ? ['text/html'] : ['text/html', '*/*'],
})
);
return (proxy
? registerProxy(devServer, proxy)
: Promise.resolve()).then(() => {
// Finally, by now we have certainly resolved the URL.
// It may be /index.html, so let the dev server try serving it again.
devServer.use(devServer.middleware);
});
}; };
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment