From b76e41d87fba5c1dddb0a0febc67d5138082f690 Mon Sep 17 00:00:00 2001 From: vic Date: Fri, 17 Mar 2017 11:49:11 +0530 Subject: [PATCH 1/8] Change proxy localhost to I27.0.0.1 for windows --- .../react-scripts/scripts/utils/addWebpackMiddleware.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/react-scripts/scripts/utils/addWebpackMiddleware.js b/packages/react-scripts/scripts/utils/addWebpackMiddleware.js index d2a97507237..eec75be94bb 100644 --- a/packages/react-scripts/scripts/utils/addWebpackMiddleware.js +++ b/packages/react-scripts/scripts/utils/addWebpackMiddleware.js @@ -59,7 +59,7 @@ function onProxyError(proxy) { module.exports = function addWebpackMiddleware(devServer) { // `proxy` lets you to specify a fallback server during development. // Every unrecognized request will be forwarded to it. - const proxy = require(paths.appPackageJson).proxy; + let proxy = require(paths.appPackageJson).proxy; devServer.use( historyApiFallback({ // Paths with dots should still use the history fallback. @@ -91,6 +91,11 @@ module.exports = function addWebpackMiddleware(devServer) { process.exit(1); } + // HACK to replace localhost with ip to overcome proxy issue on windows #1116 + if (process.platform === 'win32') { + proxy = proxy.replace('://localhost', '://127.0.0.1'); + } + // 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: // - /index.html (served as HTML5 history API fallback) From a93cf1e19810bd1aab8cb3a2a54770201df1a45d Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Wed, 22 Mar 2017 14:00:46 -0400 Subject: [PATCH 2/8] Update comment --- packages/react-scripts/scripts/utils/addWebpackMiddleware.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/react-scripts/scripts/utils/addWebpackMiddleware.js b/packages/react-scripts/scripts/utils/addWebpackMiddleware.js index eec75be94bb..a7105a70c25 100644 --- a/packages/react-scripts/scripts/utils/addWebpackMiddleware.js +++ b/packages/react-scripts/scripts/utils/addWebpackMiddleware.js @@ -91,7 +91,8 @@ module.exports = function addWebpackMiddleware(devServer) { process.exit(1); } - // HACK to replace localhost with ip to overcome proxy issue on windows #1116 + // Patches a long standing `dns.lookup()` bug on Windows. + // See https://github.com/nodejs/node-v0.x-archive/issues/25489 if (process.platform === 'win32') { proxy = proxy.replace('://localhost', '://127.0.0.1'); } From f0519cf52ae56884249712db4b94228eb00f5da5 Mon Sep 17 00:00:00 2001 From: vic Date: Mon, 3 Apr 2017 15:50:42 +0530 Subject: [PATCH 3/8] resolve localhost IP with DNS lookup on windows --- .../scripts/utils/addWebpackMiddleware.js | 119 ++++++++++++------ 1 file changed, 81 insertions(+), 38 deletions(-) diff --git a/packages/react-scripts/scripts/utils/addWebpackMiddleware.js b/packages/react-scripts/scripts/utils/addWebpackMiddleware.js index a7105a70c25..35cca904eb7 100644 --- a/packages/react-scripts/scripts/utils/addWebpackMiddleware.js +++ b/packages/react-scripts/scripts/utils/addWebpackMiddleware.js @@ -14,6 +14,7 @@ const chalk = require('chalk'); const historyApiFallback = require('connect-history-api-fallback'); const httpProxyMiddleware = require('http-proxy-middleware'); const paths = require('../../config/paths'); +const dns = require('dns'); // We need to provide a custom onError function for httpProxyMiddleware. // It allows us to log custom error messages on the console. @@ -75,7 +76,22 @@ module.exports = function addWebpackMiddleware(devServer) { htmlAcceptHeaders: proxy ? ['text/html'] : ['text/html', '*/*'], }) ); + if (proxy) { + fixProxy(proxy).then(proxy => { + console.log('proxy', proxy); + setProxy(proxy, devServer); + }); + } + + // 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); +}; + +// Test proxy value if supplied and fix issues if possible +function fixProxy(proxy) { + return new Promise((resolve, reject) => { if (typeof proxy !== 'string') { console.log( chalk.red('When specified, "proxy" in package.json must be a string.') @@ -93,46 +109,73 @@ module.exports = function addWebpackMiddleware(devServer) { // Patches a long standing `dns.lookup()` bug on Windows. // See https://github.com/nodejs/node-v0.x-archive/issues/25489 - if (process.platform === 'win32') { - proxy = proxy.replace('://localhost', '://127.0.0.1'); + // This is needed only for windows and when proxy value has localhost + if (process.platform === 'windows' && proxy.search('localhost') >= 0) { + getLocalhostIP() + .then(ip => { + proxy = proxy.replace('localhost', ip); + resolve(proxy); + }) + .catch(e => { + console.log(chalk.red('"proxy" in package.json is set to localhost')); + console.log( + chalk.red( + 'but unable to connect. Try 127.0.0.1 instead of localhost ' + ) + ); + process.exit(1); + }); + } else { + resolve(proxy); } + }); +} - // 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: - // - /index.html (served as HTML5 history API fallback) - // - /*.hot-update.json (WebpackDevServer uses this too for hot reloading) - // - /sockjs-node/* (WebpackDevServer uses this for hot reloading) - // Tip: use https://jex.im/regulex/ to visualize the regex - const mayProxy = /^(?!\/(index\.html$|.*\.hot-update\.json$|sockjs-node\/)).*$/; - - // Pass the scope regex both to Express and to the middleware for proxying - // of both HTTP and WebSockets to work without false positives. - const hpm = httpProxyMiddleware(pathname => mayProxy.test(pathname), { - target: proxy, - logLevel: 'silent', - onProxyReq: proxyReq => { - // Browers may send Origin headers even with same-origin - // requests. To prevent CORS issues, we have to change - // the Origin to match the target URL. - if (proxyReq.getHeader('origin')) { - proxyReq.setHeader('origin', proxy); - } - }, - onError: onProxyError(proxy), - secure: false, - changeOrigin: true, - ws: true, - xfwd: true, +// resolve localhost to it's IP4 or IP6 number +function getLocalhostIP() { + return new Promise((resolve, reject) => { + dns.lookup('localhost', function(err, result) { + if (err) { + reject(err); // 'Cannot resolve IP of localhost + } + resolve(result); }); - devServer.use(mayProxy, hpm); + }); +} - // Listen for the websocket 'upgrade' event and upgrade the connection. - // If this is not done, httpProxyMiddleware will not try to upgrade until - // an initial plain HTTP request is made. - devServer.listeningApp.on('upgrade', hpm.upgrade); - } +// setup proxy devServer +function setProxy(proxy, devServer) { + // 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: + // - /index.html (served as HTML5 history API fallback) + // - /*.hot-update.json (WebpackDevServer uses this too for hot reloading) + // - /sockjs-node/* (WebpackDevServer uses this for hot reloading) + // Tip: use https://jex.im/regulex/ to visualize the regex + const mayProxy = /^(?!\/(index\.html$|.*\.hot-update\.json$|sockjs-node\/)).*$/; - // 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); -}; + // Pass the scope regex both to Express and to the middleware for proxying + // of both HTTP and WebSockets to work without false positives. + const hpm = httpProxyMiddleware(pathname => mayProxy.test(pathname), { + target: proxy, + logLevel: 'silent', + onProxyReq: proxyReq => { + // Browers may send Origin headers even with same-origin + // requests. To prevent CORS issues, we have to change + // the Origin to match the target URL. + if (proxyReq.getHeader('origin')) { + proxyReq.setHeader('origin', proxy); + } + }, + onError: onProxyError(proxy), + secure: false, + changeOrigin: true, + ws: true, + xfwd: true, + }); + devServer.use(mayProxy, hpm); + + // Listen for the websocket 'upgrade' event and upgrade the connection. + // If this is not done, httpProxyMiddleware will not try to upgrade until + // an initial plain HTTP request is made. + devServer.listeningApp.on('upgrade', hpm.upgrade); +} From a74d0ae1ef0fa4921297118ecad3ed4d51d0faa5 Mon Sep 17 00:00:00 2001 From: vic Date: Tue, 4 Apr 2017 11:09:44 +0530 Subject: [PATCH 4/8] Fix CI errors --- packages/react-scripts/scripts/utils/addWebpackMiddleware.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-scripts/scripts/utils/addWebpackMiddleware.js b/packages/react-scripts/scripts/utils/addWebpackMiddleware.js index 35cca904eb7..eff4d3c3368 100644 --- a/packages/react-scripts/scripts/utils/addWebpackMiddleware.js +++ b/packages/react-scripts/scripts/utils/addWebpackMiddleware.js @@ -91,7 +91,7 @@ module.exports = function addWebpackMiddleware(devServer) { // Test proxy value if supplied and fix issues if possible function fixProxy(proxy) { - return new Promise((resolve, reject) => { + return new Promise(resolve => { if (typeof proxy !== 'string') { console.log( chalk.red('When specified, "proxy" in package.json must be a string.') @@ -116,7 +116,7 @@ function fixProxy(proxy) { proxy = proxy.replace('localhost', ip); resolve(proxy); }) - .catch(e => { + .catch(() => { console.log(chalk.red('"proxy" in package.json is set to localhost')); console.log( chalk.red( From a0bb682ec442b86e12392da575656a7844793768 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Wed, 19 Apr 2017 13:30:45 -0400 Subject: [PATCH 5/8] Promisify addWebpackMiddleware --- packages/react-scripts/scripts/start.js | 40 ++-- .../scripts/utils/addWebpackMiddleware.js | 193 ++++++++---------- 2 files changed, 113 insertions(+), 120 deletions(-) diff --git a/packages/react-scripts/scripts/start.js b/packages/react-scripts/scripts/start.js index 12cce8ed313..2ba4d2cf7a5 100644 --- a/packages/react-scripts/scripts/start.js +++ b/packages/react-scripts/scripts/start.js @@ -80,22 +80,30 @@ function run(port) { const devServer = new WebpackDevServer(compiler, devServerConfig); // Our custom middleware proxies requests to /index.html or a remote API. - addWebpackMiddleware(devServer); - - // Launch WebpackDevServer. - devServer.listen(port, err => { - if (err) { - return console.log(err); - } - - if (isInteractive) { - clearConsole(); - } - console.log(chalk.cyan('Starting the development server...')); - console.log(); - - openBrowser(`${protocol}://${host}:${port}/`); - }); + addWebpackMiddleware(devServer) + .then(() => { + // Launch WebpackDevServer. + devServer.listen(port, err => { + if (err) { + return console.log(err); + } + + if (isInteractive) { + clearConsole(); + } + console.log(chalk.cyan('Starting the development server...')); + console.log(); + + 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 diff --git a/packages/react-scripts/scripts/utils/addWebpackMiddleware.js b/packages/react-scripts/scripts/utils/addWebpackMiddleware.js index eff4d3c3368..fe21d0109ba 100644 --- a/packages/react-scripts/scripts/utils/addWebpackMiddleware.js +++ b/packages/react-scripts/scripts/utils/addWebpackMiddleware.js @@ -11,10 +11,11 @@ 'use strict'; const chalk = require('chalk'); +const dns = require('dns'); const historyApiFallback = require('connect-history-api-fallback'); const httpProxyMiddleware = require('http-proxy-middleware'); +const url = require('url'); const paths = require('../../config/paths'); -const dns = require('dns'); // We need to provide a custom onError function for httpProxyMiddleware. // It allows us to log custom error messages on the console. @@ -57,10 +58,89 @@ function onProxyError(proxy) { }; } +function resolveProxy(proxy) { + const p = url.parse(proxy); + const { hostname } = p; + if (hostname !== 'localhost') { + return Promise.resolve(proxy); + } + p.host = undefined; // Remove the host; we don't care about it + return new Promise(resolve => { + dns.lookup(hostname, { hints: 0, all: false }, (err, address) => { + if (err) { + console.log( + chalk.red( + '"proxy" in package.json is set to localhost and cannot be resolved.' + ) + ); + console.log( + chalk.red('Try setting "proxy" to 127.0.0.1 instead of localhost.') + ); + process.exit(1); + } + p.hostname = address; + resolve(url.format(p)); + }); + }); +} + +function registerProxy(devServer, proxy) { + if (typeof proxy !== 'string') { + console.log( + chalk.red('When specified, "proxy" in package.json must be a string.') + ); + console.log( + chalk.red('Instead, the type of "proxy" was "' + typeof proxy + '".') + ); + console.log( + chalk.red('Either remove "proxy" from package.json, or make it a string.') + ); + process.exit(1); + } + + return (process.platform === 'windows' + ? resolveProxy(proxy) + : Promise.resolve()).then(() => { + // 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: + // - /index.html (served as HTML5 history API fallback) + // - /*.hot-update.json (WebpackDevServer uses this too for hot reloading) + // - /sockjs-node/* (WebpackDevServer uses this for hot reloading) + // Tip: use https://jex.im/regulex/ to visualize the regex + const mayProxy = /^(?!\/(index\.html$|.*\.hot-update\.json$|sockjs-node\/)).*$/; + + // Pass the scope regex both to Express and to the middleware for proxying + // of both HTTP and WebSockets to work without false positives. + const hpm = httpProxyMiddleware(pathname => mayProxy.test(pathname), { + target: proxy, + logLevel: 'silent', + onProxyReq: proxyReq => { + // Browers may send Origin headers even with same-origin + // requests. To prevent CORS issues, we have to change + // the Origin to match the target URL. + if (proxyReq.getHeader('origin')) { + proxyReq.setHeader('origin', proxy); + } + }, + onError: onProxyError(proxy), + secure: false, + changeOrigin: true, + ws: true, + xfwd: true, + }); + devServer.use(mayProxy, hpm); + + // Listen for the websocket 'upgrade' event and upgrade the connection. + // If this is not done, httpProxyMiddleware will not try to upgrade until + // an initial plain HTTP request is made. + devServer.listeningApp.on('upgrade', hpm.upgrade); + }); +} + module.exports = function addWebpackMiddleware(devServer) { // `proxy` lets you to specify a fallback server during development. // Every unrecognized request will be forwarded to it. - let proxy = require(paths.appPackageJson).proxy; + const proxy = require(paths.appPackageJson).proxy; devServer.use( historyApiFallback({ // Paths with dots should still use the history fallback. @@ -76,106 +156,11 @@ module.exports = function addWebpackMiddleware(devServer) { htmlAcceptHeaders: proxy ? ['text/html'] : ['text/html', '*/*'], }) ); - - if (proxy) { - fixProxy(proxy).then(proxy => { - console.log('proxy', proxy); - setProxy(proxy, devServer); - }); - } - - // 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); -}; - -// Test proxy value if supplied and fix issues if possible -function fixProxy(proxy) { - return new Promise(resolve => { - if (typeof proxy !== 'string') { - console.log( - chalk.red('When specified, "proxy" in package.json must be a string.') - ); - console.log( - chalk.red('Instead, the type of "proxy" was "' + typeof proxy + '".') - ); - console.log( - chalk.red( - 'Either remove "proxy" from package.json, or make it a string.' - ) - ); - process.exit(1); - } - - // Patches a long standing `dns.lookup()` bug on Windows. - // See https://github.com/nodejs/node-v0.x-archive/issues/25489 - // This is needed only for windows and when proxy value has localhost - if (process.platform === 'windows' && proxy.search('localhost') >= 0) { - getLocalhostIP() - .then(ip => { - proxy = proxy.replace('localhost', ip); - resolve(proxy); - }) - .catch(() => { - console.log(chalk.red('"proxy" in package.json is set to localhost')); - console.log( - chalk.red( - 'but unable to connect. Try 127.0.0.1 instead of localhost ' - ) - ); - process.exit(1); - }); - } else { - resolve(proxy); - } - }); -} - -// resolve localhost to it's IP4 or IP6 number -function getLocalhostIP() { - return new Promise((resolve, reject) => { - dns.lookup('localhost', function(err, result) { - if (err) { - reject(err); // 'Cannot resolve IP of localhost - } - resolve(result); - }); - }); -} - -// setup proxy devServer -function setProxy(proxy, devServer) { - // 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: - // - /index.html (served as HTML5 history API fallback) - // - /*.hot-update.json (WebpackDevServer uses this too for hot reloading) - // - /sockjs-node/* (WebpackDevServer uses this for hot reloading) - // Tip: use https://jex.im/regulex/ to visualize the regex - const mayProxy = /^(?!\/(index\.html$|.*\.hot-update\.json$|sockjs-node\/)).*$/; - - // Pass the scope regex both to Express and to the middleware for proxying - // of both HTTP and WebSockets to work without false positives. - const hpm = httpProxyMiddleware(pathname => mayProxy.test(pathname), { - target: proxy, - logLevel: 'silent', - onProxyReq: proxyReq => { - // Browers may send Origin headers even with same-origin - // requests. To prevent CORS issues, we have to change - // the Origin to match the target URL. - if (proxyReq.getHeader('origin')) { - proxyReq.setHeader('origin', proxy); - } - }, - onError: onProxyError(proxy), - secure: false, - changeOrigin: true, - ws: true, - xfwd: true, + 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); }); - devServer.use(mayProxy, hpm); - - // Listen for the websocket 'upgrade' event and upgrade the connection. - // If this is not done, httpProxyMiddleware will not try to upgrade until - // an initial plain HTTP request is made. - devServer.listeningApp.on('upgrade', hpm.upgrade); -} +}; From f221c2881a9a4b1a1464ccf18997f5d7cc16b1dc Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Wed, 19 Apr 2017 15:54:14 -0400 Subject: [PATCH 6/8] Remove Node 6 syntax --- packages/react-scripts/scripts/utils/addWebpackMiddleware.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-scripts/scripts/utils/addWebpackMiddleware.js b/packages/react-scripts/scripts/utils/addWebpackMiddleware.js index a9bd4468606..ef2e30a1c9a 100644 --- a/packages/react-scripts/scripts/utils/addWebpackMiddleware.js +++ b/packages/react-scripts/scripts/utils/addWebpackMiddleware.js @@ -60,7 +60,7 @@ function onProxyError(proxy) { function resolveProxy(proxy) { const p = url.parse(proxy); - const { hostname } = p; + const hostname = p.hostname; if (hostname !== 'localhost') { return Promise.resolve(proxy); } From d003dc6ffa9e9a720e24911e29647ff7b304b448 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Fri, 21 Apr 2017 15:46:16 -0400 Subject: [PATCH 7/8] Update addWebpackMiddleware.js --- packages/react-scripts/scripts/utils/addWebpackMiddleware.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-scripts/scripts/utils/addWebpackMiddleware.js b/packages/react-scripts/scripts/utils/addWebpackMiddleware.js index ef2e30a1c9a..a1904ef401d 100644 --- a/packages/react-scripts/scripts/utils/addWebpackMiddleware.js +++ b/packages/react-scripts/scripts/utils/addWebpackMiddleware.js @@ -106,7 +106,7 @@ function registerProxy(devServer, proxy) { process.exit(1); } - return (process.platform === 'windows' + return (process.platform === 'win32' ? resolveProxy(proxy) : Promise.resolve()).then(() => { // Otherwise, if proxy is specified, we will let it handle any request. From ba9bad298f2e5259c901c2e424e30915f396804f Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Fri, 21 Apr 2017 15:58:27 -0400 Subject: [PATCH 8/8] Actually use the resolved proxy --- .../scripts/utils/addWebpackMiddleware.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/react-scripts/scripts/utils/addWebpackMiddleware.js b/packages/react-scripts/scripts/utils/addWebpackMiddleware.js index a1904ef401d..a3deaf34a76 100644 --- a/packages/react-scripts/scripts/utils/addWebpackMiddleware.js +++ b/packages/react-scripts/scripts/utils/addWebpackMiddleware.js @@ -84,20 +84,20 @@ function resolveProxy(proxy) { }); } -function registerProxy(devServer, proxy) { - if (typeof proxy !== 'string') { +function registerProxy(devServer, _proxy) { + if (typeof _proxy !== 'string') { console.log( chalk.red('When specified, "proxy" in package.json must be a string.') ); console.log( - chalk.red('Instead, the type of "proxy" was "' + typeof proxy + '".') + chalk.red('Instead, the type of "proxy" was "' + typeof _proxy + '".') ); console.log( chalk.red('Either remove "proxy" from package.json, or make it a string.') ); process.exit(1); // Test that proxy url specified starts with http:// or https:// - } else if (!/^http(s)?:\/\//.test(proxy)) { + } else if (!/^http(s)?:\/\//.test(_proxy)) { console.log( chalk.red( 'When "proxy" is specified in package.json it must start with either http:// or https://' @@ -107,8 +107,8 @@ function registerProxy(devServer, proxy) { } return (process.platform === 'win32' - ? resolveProxy(proxy) - : Promise.resolve()).then(() => { + ? resolveProxy(_proxy) + : Promise.resolve(_proxy)).then(proxy => { // 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: // - /index.html (served as HTML5 history API fallback)