Skip to content

Commit f10dcfa

Browse files
bunsharTimer
authored andcommitted
Resolve localhost when offline on Windows (facebook#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
1 parent ef63b0d commit f10dcfa

File tree

2 files changed

+104
-62
lines changed

2 files changed

+104
-62
lines changed

scripts/start.js

+24-16
Original file line numberDiff line numberDiff line change
@@ -80,22 +80,30 @@ function run(port) {
8080
const devServer = new WebpackDevServer(compiler, devServerConfig);
8181

8282
// Our custom middleware proxies requests to /index.html or a remote API.
83-
addWebpackMiddleware(devServer);
84-
85-
// Launch WebpackDevServer.
86-
devServer.listen(port, host, err => {
87-
if (err) {
88-
return console.log(err);
89-
}
90-
91-
if (isInteractive) {
92-
clearConsole();
93-
}
94-
console.log(chalk.cyan('Starting the development server...'));
95-
console.log();
96-
97-
openBrowser(`${protocol}://${host}:${port}/`);
98-
});
83+
addWebpackMiddleware(devServer)
84+
.then(() => {
85+
// Launch WebpackDevServer.
86+
devServer.listen(port, host, err => {
87+
if (err) {
88+
return console.log(err);
89+
}
90+
91+
if (isInteractive) {
92+
clearConsole();
93+
}
94+
console.log(chalk.cyan('Starting the development server...'));
95+
console.log();
96+
97+
openBrowser(`${protocol}://${host}:${port}/`);
98+
});
99+
})
100+
.catch(e => {
101+
console.log(
102+
chalk.red('Failed to setup middleware, please report this error:')
103+
);
104+
console.log(e);
105+
process.exit(1);
106+
});
99107
}
100108

101109
// We attempt to use the default port but if it is busy, we offer the user to

scripts/utils/addWebpackMiddleware.js

+80-46
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111
'use strict';
1212

1313
const chalk = require('chalk');
14+
const dns = require('dns');
1415
const historyApiFallback = require('connect-history-api-fallback');
1516
const httpProxyMiddleware = require('http-proxy-middleware');
17+
const url = require('url');
1618
const paths = require('../../config/paths');
1719

1820
// We need to provide a custom onError function for httpProxyMiddleware.
@@ -56,49 +58,57 @@ function onProxyError(proxy) {
5658
};
5759
}
5860

59-
module.exports = function addWebpackMiddleware(devServer) {
60-
// `proxy` lets you to specify a fallback server during development.
61-
// Every unrecognized request will be forwarded to it.
62-
const proxy = require(paths.appPackageJson).proxy;
63-
devServer.use(
64-
historyApiFallback({
65-
// Paths with dots should still use the history fallback.
66-
// See https://github.com/facebookincubator/create-react-app/issues/387.
67-
disableDotRule: true,
68-
// For single page apps, we generally want to fallback to /index.html.
69-
// However we also want to respect `proxy` for API calls.
70-
// So if `proxy` is specified, we need to decide which fallback to use.
71-
// We use a heuristic: if request `accept`s text/html, we pick /index.html.
72-
// Modern browsers include text/html into `accept` header when navigating.
73-
// However API calls like `fetch()` won’t generally accept text/html.
74-
// If this heuristic doesn’t work well for you, don’t use `proxy`.
75-
htmlAcceptHeaders: proxy ? ['text/html'] : ['text/html', '*/*'],
76-
})
77-
);
78-
if (proxy) {
79-
if (typeof proxy !== 'string') {
80-
console.log(
81-
chalk.red('When specified, "proxy" in package.json must be a string.')
82-
);
83-
console.log(
84-
chalk.red('Instead, the type of "proxy" was "' + typeof proxy + '".')
85-
);
86-
console.log(
87-
chalk.red(
88-
'Either remove "proxy" from package.json, or make it a string.'
89-
)
90-
);
91-
process.exit(1);
92-
// Test that proxy url specified starts with http:// or https://
93-
} else if (!/^http(s)?:\/\//.test(proxy)) {
94-
console.log(
95-
chalk.red(
96-
'When "proxy" is specified in package.json it must start with either http:// or https://'
97-
)
98-
);
99-
process.exit(1);
100-
}
61+
function resolveProxy(proxy) {
62+
const p = url.parse(proxy);
63+
const hostname = p.hostname;
64+
if (hostname !== 'localhost') {
65+
return Promise.resolve(proxy);
66+
}
67+
p.host = undefined; // Remove the host; we don't care about it
68+
return new Promise(resolve => {
69+
dns.lookup(hostname, { hints: 0, all: false }, (err, address) => {
70+
if (err) {
71+
console.log(
72+
chalk.red(
73+
'"proxy" in package.json is set to localhost and cannot be resolved.'
74+
)
75+
);
76+
console.log(
77+
chalk.red('Try setting "proxy" to 127.0.0.1 instead of localhost.')
78+
);
79+
process.exit(1);
80+
}
81+
p.hostname = address;
82+
resolve(url.format(p));
83+
});
84+
});
85+
}
86+
87+
function registerProxy(devServer, _proxy) {
88+
if (typeof _proxy !== 'string') {
89+
console.log(
90+
chalk.red('When specified, "proxy" in package.json must be a string.')
91+
);
92+
console.log(
93+
chalk.red('Instead, the type of "proxy" was "' + typeof _proxy + '".')
94+
);
95+
console.log(
96+
chalk.red('Either remove "proxy" from package.json, or make it a string.')
97+
);
98+
process.exit(1);
99+
// Test that proxy url specified starts with http:// or https://
100+
} else if (!/^http(s)?:\/\//.test(_proxy)) {
101+
console.log(
102+
chalk.red(
103+
'When "proxy" is specified in package.json it must start with either http:// or https://'
104+
)
105+
);
106+
process.exit(1);
107+
}
101108

109+
return (process.platform === 'win32'
110+
? resolveProxy(_proxy)
111+
: Promise.resolve(_proxy)).then(proxy => {
102112
// Otherwise, if proxy is specified, we will let it handle any request.
103113
// There are a few exceptions which we won't send to the proxy:
104114
// - /index.html (served as HTML5 history API fallback)
@@ -132,9 +142,33 @@ module.exports = function addWebpackMiddleware(devServer) {
132142
// If this is not done, httpProxyMiddleware will not try to upgrade until
133143
// an initial plain HTTP request is made.
134144
devServer.listeningApp.on('upgrade', hpm.upgrade);
135-
}
145+
});
146+
}
136147

137-
// Finally, by now we have certainly resolved the URL.
138-
// It may be /index.html, so let the dev server try serving it again.
139-
devServer.use(devServer.middleware);
148+
module.exports = function addWebpackMiddleware(devServer) {
149+
// `proxy` lets you to specify a fallback server during development.
150+
// Every unrecognized request will be forwarded to it.
151+
const proxy = require(paths.appPackageJson).proxy;
152+
devServer.use(
153+
historyApiFallback({
154+
// Paths with dots should still use the history fallback.
155+
// See https://github.com/facebookincubator/create-react-app/issues/387.
156+
disableDotRule: true,
157+
// For single page apps, we generally want to fallback to /index.html.
158+
// However we also want to respect `proxy` for API calls.
159+
// So if `proxy` is specified, we need to decide which fallback to use.
160+
// We use a heuristic: if request `accept`s text/html, we pick /index.html.
161+
// Modern browsers include text/html into `accept` header when navigating.
162+
// However API calls like `fetch()` won’t generally accept text/html.
163+
// If this heuristic doesn’t work well for you, don’t use `proxy`.
164+
htmlAcceptHeaders: proxy ? ['text/html'] : ['text/html', '*/*'],
165+
})
166+
);
167+
return (proxy
168+
? registerProxy(devServer, proxy)
169+
: Promise.resolve()).then(() => {
170+
// Finally, by now we have certainly resolved the URL.
171+
// It may be /index.html, so let the dev server try serving it again.
172+
devServer.use(devServer.middleware);
173+
});
140174
};

0 commit comments

Comments
 (0)