From 2f79303fd2a295ecb8f2f4d7c618dd8e2922adf2 Mon Sep 17 00:00:00 2001 From: James Blight Date: Sat, 11 Mar 2017 13:33:04 +1030 Subject: [PATCH 1/6] Change proxy handling to allow multiple proxies to be specified in package.json. --- .../config/webpackDevServer.config.js | 8 + packages/react-scripts/scripts/start.js | 4 - .../scripts/utils/addWebpackMiddleware.js | 132 ---------------- .../scripts/utils/prepareProxy.js | 149 ++++++++++++++++++ 4 files changed, 157 insertions(+), 136 deletions(-) delete mode 100644 packages/react-scripts/scripts/utils/addWebpackMiddleware.js create mode 100644 packages/react-scripts/scripts/utils/prepareProxy.js diff --git a/packages/react-scripts/config/webpackDevServer.config.js b/packages/react-scripts/config/webpackDevServer.config.js index 37d9d1a750e..effbf728433 100644 --- a/packages/react-scripts/config/webpackDevServer.config.js +++ b/packages/react-scripts/config/webpackDevServer.config.js @@ -12,6 +12,8 @@ const config = require('./webpack.config.dev'); const paths = require('./paths'); +const proxy = require(paths.appPackageJson).proxy; +const prepareProxy = require('../scripts/utils/prepareProxy'); const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'; const host = process.env.HOST || 'localhost'; @@ -60,4 +62,10 @@ module.exports = { https: protocol === 'https', host: host, overlay: false, + historyApiFallback: { + // Paths with dots should still use the history fallback. + // See https://github.com/facebookincubator/create-react-app/issues/387. + disableDotRule: true, + }, + proxy: prepareProxy(proxy), }; diff --git a/packages/react-scripts/scripts/start.js b/packages/react-scripts/scripts/start.js index 142a1f6d705..57d898e1e2b 100644 --- a/packages/react-scripts/scripts/start.js +++ b/packages/react-scripts/scripts/start.js @@ -31,7 +31,6 @@ const paths = require('../config/paths'); const config = require('../config/webpack.config.dev'); const devServerConfig = require('../config/webpackDevServer.config'); const createWebpackCompiler = require('./utils/createWebpackCompiler'); -const addWebpackMiddleware = require('./utils/addWebpackMiddleware'); const useYarn = fs.existsSync(paths.yarnLockFile); const cli = useYarn ? 'yarn' : 'npm'; @@ -72,9 +71,6 @@ function run(port) { // Serve webpack assets generated by the compiler over a web sever. 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) { diff --git a/packages/react-scripts/scripts/utils/addWebpackMiddleware.js b/packages/react-scripts/scripts/utils/addWebpackMiddleware.js deleted file mode 100644 index d2a97507237..00000000000 --- a/packages/react-scripts/scripts/utils/addWebpackMiddleware.js +++ /dev/null @@ -1,132 +0,0 @@ -// @remove-on-eject-begin -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ -// @remove-on-eject-end -'use strict'; - -const chalk = require('chalk'); -const historyApiFallback = require('connect-history-api-fallback'); -const httpProxyMiddleware = require('http-proxy-middleware'); -const paths = require('../../config/paths'); - -// We need to provide a custom onError function for httpProxyMiddleware. -// It allows us to log custom error messages on the console. -function onProxyError(proxy) { - return (err, req, res) => { - const host = req.headers && req.headers.host; - console.log( - chalk.red('Proxy error:') + - ' Could not proxy request ' + - chalk.cyan(req.url) + - ' from ' + - chalk.cyan(host) + - ' to ' + - chalk.cyan(proxy) + - '.' - ); - console.log( - 'See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (' + - chalk.cyan(err.code) + - ').' - ); - console.log(); - - // And immediately send the proper error response to the client. - // Otherwise, the request will eventually timeout with ERR_EMPTY_RESPONSE on the client side. - if (res.writeHead && !res.headersSent) { - res.writeHead(500); - } - res.end( - 'Proxy error: Could not proxy request ' + - req.url + - ' from ' + - host + - ' to ' + - proxy + - ' (' + - err.code + - ').' - ); - }; -} - -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; - 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', '*/*'], - }) - ); - if (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); - } - - // 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); - } - - // 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); -}; diff --git a/packages/react-scripts/scripts/utils/prepareProxy.js b/packages/react-scripts/scripts/utils/prepareProxy.js new file mode 100644 index 00000000000..88529ca5748 --- /dev/null +++ b/packages/react-scripts/scripts/utils/prepareProxy.js @@ -0,0 +1,149 @@ +// @remove-on-eject-begin +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +// @remove-on-eject-end +'use strict'; + +const chalk = require('chalk'); + +// We need to provide a custom onError function for httpProxyMiddleware. +// It allows us to log custom error messages on the console. +function onProxyError(proxy) { + return (err, req, res) => { + const host = req.headers && req.headers.host; + console.log( + chalk.red('Proxy error:') + + ' Could not proxy request ' + + chalk.cyan(req.url) + + ' from ' + + chalk.cyan(host) + + ' to ' + + chalk.cyan(proxy) + + '.' + ); + console.log( + 'See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (' + + chalk.cyan(err.code) + + ').' + ); + console.log(); + + // And immediately send the proper error response to the client. + // Otherwise, the request will eventually timeout with ERR_EMPTY_RESPONSE on the client side. + if (res.writeHead && !res.headersSent) { + res.writeHead(500); + } + res.end( + 'Proxy error: Could not proxy request ' + + req.url + + ' from ' + + host + + ' to ' + + proxy + + ' (' + + err.code + + ').' + ); + }; +} + +module.exports = function prepareProxy(proxy) { + // `proxy` lets you specify alternate servers for specific requests. + // It can either be a string or an object conforming to the Webpack dev server proxy configuration + // https://webpack.github.io/docs/webpack-dev-server.html + if (proxy) { + if (typeof proxy !== 'object' && typeof proxy !== 'string') { + console.log( + chalk.red( + 'When specified, "proxy" in package.json must be a string or an object.' + ) + ); + 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 an object.' + ) + ); + process.exit(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) + // - /*.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\/)).*$/; + + // Support proxy as a string for those who are using the simple proxy option + if (typeof proxy === 'string') { + return [ + { + target: proxy, + logLevel: 'silent', + // 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 as a string, 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, use a custom `proxy` object. + context: function(pathname, req) { + return mayProxy.test(pathname) && + req.headers.accept && + req.headers.accept.indexOf('text/html') === -1; + }, + 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, + }, + ]; + } + + // Otherwise, proxy is an object so create an array of proxies to pass to webpackDevServer + return Object.keys(proxy).map(function(context) { + if (!proxy[context].hasOwnProperty('target')) { + console.log( + chalk.red( + 'When `proxy` in package.json is as an object, each `context` object must have a ' + + '`target` property specified as a url string' + ) + ); + process.exit(1); + } + + return Object.assign({}, proxy[context], { + context: function(pathname) { + return mayProxy.test(pathname) && pathname.match(context); + }, + 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[context].target); + } + }, + onError: onProxyError(proxy[context].target), + }); + }); + } +}; From 6f4dd69587f82d33670dd27c923d4c50a10c8cc0 Mon Sep 17 00:00:00 2001 From: James Blight Date: Fri, 24 Mar 2017 15:02:15 +1030 Subject: [PATCH 2/6] Up webpack-dev-server to 2.4.2 Webpack Dev Server version 2.4.2 handles the external websocket upgrade for all proxies --- packages/react-scripts/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json index 59542d4361d..a2f127dfb41 100644 --- a/packages/react-scripts/package.json +++ b/packages/react-scripts/package.json @@ -57,7 +57,7 @@ "style-loader": "0.13.2", "url-loader": "0.5.8", "webpack": "2.2.1", - "webpack-dev-server": "2.4.1", + "webpack-dev-server": "2.4.2", "webpack-manifest-plugin": "1.1.0", "whatwg-fetch": "2.0.2" }, From a1d9c3111402340e87d578053fbb315ea8a36869 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Sun, 14 May 2017 20:42:10 +0100 Subject: [PATCH 3/6] Fix the listen() call --- packages/react-scripts/config/webpackDevServer.config.js | 3 --- packages/react-scripts/scripts/start.js | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/react-scripts/config/webpackDevServer.config.js b/packages/react-scripts/config/webpackDevServer.config.js index da9e6d1ee5e..399ad4ca9d7 100644 --- a/packages/react-scripts/config/webpackDevServer.config.js +++ b/packages/react-scripts/config/webpackDevServer.config.js @@ -68,9 +68,6 @@ module.exports = function(proxy) { disableDotRule: true, }, proxy, - // TODO: figure out why we needed to add this. - // https://github.com/webpack/webpack-dev-server/issues/882 - disableHostCheck: true, setup(app) { // This lets us open files from the crash overlay. app.use(function launchEditorMiddleware(req, res, next) { diff --git a/packages/react-scripts/scripts/start.js b/packages/react-scripts/scripts/start.js index ae11f9fa1fb..815bd0ae6d5 100644 --- a/packages/react-scripts/scripts/start.js +++ b/packages/react-scripts/scripts/start.js @@ -78,7 +78,7 @@ function run(port) { const devServer = new WebpackDevServer(compiler, devServerConfig(proxy)); // Launch WebpackDevServer. - devServer.listen(port, err => { + devServer.listen(port, host, err => { if (err) { return console.log(err); } From 226088b265a538a8bd356f887d63152b30779b20 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Sun, 14 May 2017 16:10:46 -0400 Subject: [PATCH 4/6] Switch to correct default host --- packages/react-scripts/config/webpackDevServer.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-scripts/config/webpackDevServer.config.js b/packages/react-scripts/config/webpackDevServer.config.js index 399ad4ca9d7..d8c1b885c25 100644 --- a/packages/react-scripts/config/webpackDevServer.config.js +++ b/packages/react-scripts/config/webpackDevServer.config.js @@ -15,7 +15,7 @@ const config = require('./webpack.config.dev'); const paths = require('./paths'); const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'; -const host = process.env.HOST || 'localhost'; +const host = process.env.HOST || '0.0.0.0'; module.exports = function(proxy) { return { From 2e099d8c81251195063bbbe2c203d387b55e64f1 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Sun, 14 May 2017 17:18:53 -0400 Subject: [PATCH 5/6] Remove promises and extract to react-dev-utils --- packages/react-dev-utils/package.json | 1 + packages/react-dev-utils/prepareProxy.js | 174 ++++++++++++++++ packages/react-scripts/package.json | 3 +- packages/react-scripts/scripts/start.js | 33 +-- .../scripts/utils/prepareProxy.js | 193 ------------------ 5 files changed, 195 insertions(+), 209 deletions(-) create mode 100644 packages/react-dev-utils/prepareProxy.js delete mode 100644 packages/react-scripts/scripts/utils/prepareProxy.js diff --git a/packages/react-dev-utils/package.json b/packages/react-dev-utils/package.json index 9edb99875c4..eaed656d477 100644 --- a/packages/react-dev-utils/package.json +++ b/packages/react-dev-utils/package.json @@ -27,6 +27,7 @@ "webpackHotDevClient.js" ], "dependencies": { + "address": "1.0.1", "anser": "1.1.0", "babel-code-frame": "6.20.0", "chalk": "1.1.3", diff --git a/packages/react-dev-utils/prepareProxy.js b/packages/react-dev-utils/prepareProxy.js new file mode 100644 index 00000000000..346a11f454b --- /dev/null +++ b/packages/react-dev-utils/prepareProxy.js @@ -0,0 +1,174 @@ +// @remove-on-eject-begin +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +// @remove-on-eject-end +'use strict'; + +const address = require('address'); +const chalk = require('chalk'); +const url = require('url'); + +function resolveLoopback(proxy) { + const o = url.parse(proxy); + o.host = undefined; + if (o.hostname !== 'localhost') return proxy; + try { + o.hostname = address.ipv6() ? '::1' : '127.0.0.1'; + } catch (_ignored) { + o.hostname = '127.0.0.1'; + } + return url.format(o); +} + +// We need to provide a custom onError function for httpProxyMiddleware. +// It allows us to log custom error messages on the console. +function onProxyError(proxy) { + return (err, req, res) => { + const host = req.headers && req.headers.host; + console.log( + chalk.red('Proxy error:') + + ' Could not proxy request ' + + chalk.cyan(req.url) + + ' from ' + + chalk.cyan(host) + + ' to ' + + chalk.cyan(proxy) + + '.' + ); + console.log( + 'See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (' + + chalk.cyan(err.code) + + ').' + ); + console.log(); + + // And immediately send the proper error response to the client. + // Otherwise, the request will eventually timeout with ERR_EMPTY_RESPONSE on the client side. + if (res.writeHead && !res.headersSent) { + res.writeHead(500); + } + res.end( + 'Proxy error: Could not proxy request ' + + req.url + + ' from ' + + host + + ' to ' + + proxy + + ' (' + + err.code + + ').' + ); + }; +} + +module.exports = function prepareProxy(proxy) { + // `proxy` lets you specify alternate servers for specific requests. + // It can either be a string or an object conforming to the Webpack dev server proxy configuration + // https://webpack.github.io/docs/webpack-dev-server.html + if (!proxy) return undefined; + if (typeof proxy !== 'object' && typeof proxy !== 'string') { + console.log( + chalk.red( + 'When specified, "proxy" in package.json must be a string or an object.' + ) + ); + 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 an object.' + ) + ); + process.exit(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) + // - /*.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\/)).*$/; + + // Support proxy as a string for those who are using the simple proxy option + if (typeof proxy === 'string') { + if (!/^http(s)?:\/\//.test(proxy)) { + console.log( + chalk.red( + 'When "proxy" is specified in package.json it must start with either http:// or https://' + ) + ); + process.exit(1); + } + + if (process.platform === 'win32') proxy = resolveLoopback(proxy); + return [ + { + target: proxy, + logLevel: 'silent', + // 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 as a string, 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, use a custom `proxy` object. + context: function(pathname, req) { + return mayProxy.test(pathname) && + req.headers.accept && + req.headers.accept.indexOf('text/html') === -1; + }, + 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, + }, + ]; + } + + // Otherwise, proxy is an object so create an array of proxies to pass to webpackDevServer + return Object.keys(proxy).map(function(context) { + if (!proxy[context].hasOwnProperty('target')) { + console.log( + chalk.red( + 'When `proxy` in package.json is as an object, each `context` object must have a ' + + '`target` property specified as a url string' + ) + ); + process.exit(1); + } + let target = proxy[context].target; + if (process.platform === 'win32') target = resolveLoopback(target); + return Object.assign({}, proxy[context], { + context: function(pathname) { + return mayProxy.test(pathname) && pathname.match(context); + }, + 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', target); + } + }, + target, + onError: onProxyError(target), + }); + }); +}; diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json index 86961ff905a..64f9a9e48f9 100644 --- a/packages/react-scripts/package.json +++ b/packages/react-scripts/package.json @@ -66,5 +66,6 @@ }, "optionalDependencies": { "fsevents": "1.0.17" - } + }, + "proxy": "http://localhost:5000" } diff --git a/packages/react-scripts/scripts/start.js b/packages/react-scripts/scripts/start.js index 815bd0ae6d5..d1d8985c3b9 100644 --- a/packages/react-scripts/scripts/start.js +++ b/packages/react-scripts/scripts/start.js @@ -35,7 +35,7 @@ const paths = require('../config/paths'); const config = require('../config/webpack.config.dev'); const devServerConfig = require('../config/webpackDevServer.config'); const createWebpackCompiler = require('./utils/createWebpackCompiler'); -const prepareProxy = require('./utils/prepareProxy'); +const prepareProxy = require('react-dev-utils/prepareProxy'); const useYarn = fs.existsSync(paths.yarnLockFile); const cli = useYarn ? 'yarn' : 'npm'; @@ -73,24 +73,27 @@ function run(port) { } ); + // Load proxy config + const proxy = require(paths.appPackageJson).proxy; // Serve webpack assets generated by the compiler over a web sever. - prepareProxy().then(proxy => { - const devServer = new WebpackDevServer(compiler, devServerConfig(proxy)); + const devServer = new WebpackDevServer( + compiler, + devServerConfig(prepareProxy(proxy)) + ); - // Launch WebpackDevServer. - devServer.listen(port, host, err => { - if (err) { - return console.log(err); - } + // Launch WebpackDevServer. + devServer.listen(port, host, err => { + if (err) { + return console.log(err); + } - if (isInteractive) { - clearConsole(); - } - console.log(chalk.cyan('Starting the development server...')); - console.log(); + if (isInteractive) { + clearConsole(); + } + console.log(chalk.cyan('Starting the development server...')); + console.log(); - openBrowser(`${protocol}://${host}:${port}/`); - }); + openBrowser(`${protocol}://${host}:${port}/`); }); } diff --git a/packages/react-scripts/scripts/utils/prepareProxy.js b/packages/react-scripts/scripts/utils/prepareProxy.js deleted file mode 100644 index 7b2b8b25100..00000000000 --- a/packages/react-scripts/scripts/utils/prepareProxy.js +++ /dev/null @@ -1,193 +0,0 @@ -// @remove-on-eject-begin -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ -// @remove-on-eject-end -'use strict'; - -const chalk = require('chalk'); -const url = require('url'); -const dns = require('dns'); -const paths = require('../../config/paths'); -const proxy = require(paths.appPackageJson).proxy; - -function resolveProxy(proxy) { - const p = url.parse(proxy); - const hostname = p.hostname; - 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)); - }); - }); -} - -// We need to provide a custom onError function for httpProxyMiddleware. -// It allows us to log custom error messages on the console. -function onProxyError(proxy) { - return (err, req, res) => { - const host = req.headers && req.headers.host; - console.log( - chalk.red('Proxy error:') + - ' Could not proxy request ' + - chalk.cyan(req.url) + - ' from ' + - chalk.cyan(host) + - ' to ' + - chalk.cyan(proxy) + - '.' - ); - console.log( - 'See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (' + - chalk.cyan(err.code) + - ').' - ); - console.log(); - - // And immediately send the proper error response to the client. - // Otherwise, the request will eventually timeout with ERR_EMPTY_RESPONSE on the client side. - if (res.writeHead && !res.headersSent) { - res.writeHead(500); - } - res.end( - 'Proxy error: Could not proxy request ' + - req.url + - ' from ' + - host + - ' to ' + - proxy + - ' (' + - err.code + - ').' - ); - }; -} - -module.exports = function prepareProxy() { - // `proxy` lets you specify alternate servers for specific requests. - // It can either be a string or an object conforming to the Webpack dev server proxy configuration - // https://webpack.github.io/docs/webpack-dev-server.html - if (proxy) { - if (typeof proxy !== 'object' && typeof proxy !== 'string') { - console.log( - chalk.red( - 'When specified, "proxy" in package.json must be a string or an object.' - ) - ); - 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 an object.' - ) - ); - process.exit(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) - // - /*.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\/)).*$/; - - // Support proxy as a string for those who are using the simple proxy option - if (typeof proxy === 'string') { - return (process.platform === 'win32' - ? resolveProxy(proxy) - : Promise.resolve(proxy)).then(target => { - return [ - { - target, - logLevel: 'silent', - // 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 as a string, 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, use a custom `proxy` object. - context: function(pathname, req) { - return mayProxy.test(pathname) && - req.headers.accept && - req.headers.accept.indexOf('text/html') === -1; - }, - 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, - }, - ]; - }); - } - - // Otherwise, proxy is an object so create an array of proxies to pass to webpackDevServer - return Promise.all( - Object.keys(proxy).map(function(context) { - if (!proxy[context].hasOwnProperty('target')) { - console.log( - chalk.red( - 'When `proxy` in package.json is as an object, each `context` object must have a ' + - '`target` property specified as a url string' - ) - ); - process.exit(1); - } - - return (process.platform === 'win32' - ? resolveProxy(proxy[context].target) - : Promise.resolve(proxy[context].target)).then(target => { - return Object.assign({}, proxy[context], { - context: function(pathname) { - return mayProxy.test(pathname) && pathname.match(context); - }, - 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', target); - } - }, - target, - onError: onProxyError(target), - }); - }); - }) - ); - } else { - // No proxy has been specified - return Promise.resolve(); - } -}; From 9e855fdfdd129157b844fa05723cf1486e11835a Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Sun, 14 May 2017 19:51:22 -0400 Subject: [PATCH 6/6] oops --- packages/react-dev-utils/package.json | 1 + packages/react-dev-utils/prepareProxy.js | 25 +++++++++++++++++------- packages/react-scripts/package.json | 3 +-- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/packages/react-dev-utils/package.json b/packages/react-dev-utils/package.json index 538d879431a..a59993e9da5 100644 --- a/packages/react-dev-utils/package.json +++ b/packages/react-dev-utils/package.json @@ -22,6 +22,7 @@ "launchEditor.js", "openBrowser.js", "openChrome.applescript", + "prepareProxy.js", "WatchMissingNodeModulesPlugin.js", "webpackHotDevClient.js" ], diff --git a/packages/react-dev-utils/prepareProxy.js b/packages/react-dev-utils/prepareProxy.js index 346a11f454b..ea0e9f163b0 100644 --- a/packages/react-dev-utils/prepareProxy.js +++ b/packages/react-dev-utils/prepareProxy.js @@ -17,7 +17,9 @@ const url = require('url'); function resolveLoopback(proxy) { const o = url.parse(proxy); o.host = undefined; - if (o.hostname !== 'localhost') return proxy; + if (o.hostname !== 'localhost') { + return proxy; + } try { o.hostname = address.ipv6() ? '::1' : '127.0.0.1'; } catch (_ignored) { @@ -108,10 +110,15 @@ module.exports = function prepareProxy(proxy) { process.exit(1); } - if (process.platform === 'win32') proxy = resolveLoopback(proxy); + let target; + if (process.platform === 'win32') { + target = resolveLoopback(proxy); + } else { + target = proxy; + } return [ { - target: proxy, + target, logLevel: 'silent', // For single page apps, we generally want to fallback to /index.html. // However we also want to respect `proxy` for API calls. @@ -130,10 +137,10 @@ module.exports = function prepareProxy(proxy) { // requests. To prevent CORS issues, we have to change // the Origin to match the target URL. if (proxyReq.getHeader('origin')) { - proxyReq.setHeader('origin', proxy); + proxyReq.setHeader('origin', target); } }, - onError: onProxyError(proxy), + onError: onProxyError(target), secure: false, changeOrigin: true, ws: true, @@ -153,8 +160,12 @@ module.exports = function prepareProxy(proxy) { ); process.exit(1); } - let target = proxy[context].target; - if (process.platform === 'win32') target = resolveLoopback(target); + let target; + if (process.platform === 'win32') { + target = resolveLoopback(proxy[context].target); + } else { + target = proxy[context].target; + } return Object.assign({}, proxy[context], { context: function(pathname) { return mayProxy.test(pathname) && pathname.match(context); diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json index b494928748b..9208b713878 100644 --- a/packages/react-scripts/package.json +++ b/packages/react-scripts/package.json @@ -67,6 +67,5 @@ }, "optionalDependencies": { "fsevents": "1.0.17" - }, - "proxy": "http://localhost:5000" + } }