Skip to content

Commit 06dad30

Browse files
committed
refactor(serve): use http-proxy-middleware
This affects Ionic 1 serve only. This should fix some issues where the host is unexpectedly changed. `livereload.js` is now proxied through the standard HTTP server and given the host and livereload port dynamically, as it is not possible to know the host being used without JS.
1 parent b4e5ade commit 06dad30

7 files changed

Lines changed: 94 additions & 69 deletions

File tree

packages/@ionic/cli-utils/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,13 @@
5050
"diff": "^3.4.0",
5151
"elementtree": "^0.1.7",
5252
"express": "^4.16.2",
53+
"http-proxy-middleware": "^0.17.4",
5354
"inquirer": "^3.3.0",
5455
"leek": "0.0.24",
5556
"lodash": "^4.17.4",
5657
"minimist": "^1.2.0",
5758
"opn": "^5.1.0",
5859
"os-name": "^2.0.1",
59-
"proxy-middleware": "^0.15.0",
6060
"semver": "^5.4.1",
6161
"slice-ansi": "^1.0.0",
6262
"ssh-config": "^1.1.1",
@@ -81,6 +81,7 @@
8181
"@types/diff": "^3.2.2",
8282
"@types/express": "^4.0.38",
8383
"@types/gulp": "^3.8.33",
84+
"@types/http-proxy-middleware": "^0.17.2",
8485
"@types/inquirer": "0.0.35",
8586
"@types/lodash": "^4.14.80",
8687
"@types/minimist": "^1.2.0",

packages/@ionic/cli-utils/src/definitions.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ export interface SuperAgentError extends Error {
2121
response: superagentType.Response;
2222
}
2323

24-
export type LogFn = (msg: string | (() => string)) => void;
24+
export type LogMsg = string | (() => string);
25+
export type LogFn = (msg: LogMsg) => void;
2526
export type LogLevel = 'debug' | 'info' | 'ok' | 'warn' | 'error' | 'announce';
2627
export type LogPrefix = string | (() => string);
2728

@@ -43,6 +44,7 @@ export interface ILogger {
4344
error: LogFn;
4445
announce: LogFn;
4546
msg: LogFn;
47+
log: LogFn;
4648
nl(num?: number): void;
4749
shouldLog(level: LogLevel): boolean;
4850
}
@@ -90,10 +92,14 @@ export interface BowerJson {
9092
}
9193

9294
export interface ProjectFileProxy {
95+
path: string;
9396
proxyUrl: string;
9497
proxyNoAgent: boolean;
98+
99+
/**
100+
* @deprecated
101+
*/
95102
rejectUnauthorized: boolean;
96-
path: string;
97103
}
98104

99105
export type ProjectType = 'ionic-angular' | 'ionic1' | 'custom';

packages/@ionic/cli-utils/src/lib/dev-server.ts

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ import * as path from 'path';
22

33
import { IonicEnvironment, LiveReloadFunction } from '../definitions';
44

5-
import { BIND_ALL_ADDRESS } from './serve';
6-
75
export const DEV_SERVER_PREFIX = '__ionic';
86

97
export function injectDevServerScript(content: any): string {
@@ -29,8 +27,10 @@ export function injectDevServerScript(content: any): string {
2927
}
3028

3129
function getDevServerScript() {
32-
return ` <!-- Ionic Dev Server: Injected Dev Server Script -->\n` +
33-
` <script src="${DEV_SERVER_PREFIX}/dev-server.js" async="" defer=""></script>`;
30+
return `
31+
<!-- Ionic Dev Server: Injected Dev Server Script -->
32+
<script src="${DEV_SERVER_PREFIX}/dev-server.js" async="" defer=""></script>
33+
`;
3434
}
3535

3636
export async function createLiveReloadServer(env: IonicEnvironment, { port, wwwDir }: { port: number; wwwDir: string; }): Promise<LiveReloadFunction> {
@@ -49,35 +49,39 @@ export async function createLiveReloadServer(env: IonicEnvironment, { port, wwwD
4949
};
5050
}
5151

52-
export function injectLiveReloadScript(content: any, host: string, port: number): string {
53-
let contentStr = content.toString();
54-
const liveReloadScript = getLiveReloadScript(host, port);
52+
export function injectLiveReloadScript(content: string, port: number): string {
53+
const liveReloadScript = getLiveReloadScript(port);
5554

56-
if (contentStr.indexOf('/livereload.js') > -1) {
55+
if (content.indexOf('/livereload.js') > -1) {
5756
// already added script
5857
return content;
5958
}
6059

61-
let match = contentStr.match(/<\/body>(?![\s\S]*<\/body>)/i);
60+
let match = content.match(/<\/body>(?![\s\S]*<\/body>)/i);
6261
if (!match) {
63-
match = contentStr.match(/<\/html>(?![\s\S]*<\/html>)/i);
62+
match = content.match(/<\/html>(?![\s\S]*<\/html>)/i);
6463
}
6564
if (match) {
66-
contentStr = contentStr.replace(match[0], `${liveReloadScript}\n${match[0]}`);
65+
content = content.replace(match[0], `${liveReloadScript}\n${match[0]}`);
6766
} else {
68-
contentStr += liveReloadScript;
67+
content += liveReloadScript;
6968
}
7069

71-
return contentStr;
70+
return content;
7271
}
7372

74-
function getLiveReloadScript(host: string, port: number) {
75-
if (host === BIND_ALL_ADDRESS) {
76-
host = 'localhost';
77-
}
78-
79-
const src = `//${host}:${port}/livereload.js?snipver=1`;
80-
81-
return ` <!-- Ionic Dev Server: Injected LiveReload Script -->\n` +
82-
` <script src="${src}" async="" defer=""></script>`;
73+
function getLiveReloadScript(port: number) {
74+
const src = `${DEV_SERVER_PREFIX}/tiny-lr/livereload.js`;
75+
76+
return `
77+
<!-- Ionic Dev Server: Injected LiveReload Script -->
78+
<script>
79+
window.LiveReloadOptions = {
80+
host: window.location.hostname,
81+
port: ${port},
82+
snipver: true,
83+
};
84+
</script>
85+
<script src="${src}" async="" defer=""></script>
86+
`;
8387
}

packages/@ionic/cli-utils/src/lib/ionic1/serve.ts

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as util from 'util';
44
import chalk from 'chalk';
55

66
import * as expressType from 'express';
7+
import * as proxyMiddlewareType from 'http-proxy-middleware';
78

89
import { IonicEnvironment, LiveReloadFunction, LogLevel, ServeDetails, ServeOptions } from '../../definitions';
910
import { isDevServerMessage } from '../../guards';
@@ -130,10 +131,20 @@ async function setupServer(env: IonicEnvironment, options: ServeMetaOptions): Pr
130131
*/
131132
async function createHttpServer(env: IonicEnvironment, options: ServeMetaOptions): Promise<expressType.Application> {
132133
const { DEV_SERVER_PREFIX, injectDevServerScript, injectLiveReloadScript } = await import('../dev-server');
133-
const [ WebSocket, express ] = await Promise.all([import('ws'), import('express')]);
134134
const { LOGGER_STATUS_COLORS } = await import('../../lib/utils/logger');
135135

136+
const [
137+
WebSocket,
138+
express,
139+
proxyMiddleware,
140+
] = await Promise.all([
141+
import('ws'),
142+
import('express'),
143+
import('http-proxy-middleware'),
144+
]);
145+
136146
const app = express();
147+
const project = await env.project.load();
137148

138149
/**
139150
* http responder for /index.html base entrypoint
@@ -146,7 +157,7 @@ async function createHttpServer(env: IonicEnvironment, options: ServeMetaOptions
146157
indexHtml = injectDevServerScript(indexHtml);
147158

148159
if (options.livereload) {
149-
indexHtml = injectLiveReloadScript(indexHtml, options.externalAddressRequired ? options.externalIP : 'localhost', options.livereloadPort);
160+
indexHtml = injectLiveReloadScript(indexHtml, options.livereloadPort);
150161
}
151162

152163
res.set('Content-Type', 'text/html');
@@ -197,6 +208,11 @@ async function createHttpServer(env: IonicEnvironment, options: ServeMetaOptions
197208
});
198209
}
199210

211+
app.use((req, res, next) => {
212+
env.log.debug(`${req.method} ${req.path}`);
213+
next();
214+
});
215+
200216
app.get('/', serveIndex);
201217
app.use('/', express.static(options.wwwDir));
202218

@@ -217,8 +233,27 @@ async function createHttpServer(env: IonicEnvironment, options: ServeMetaOptions
217233
app.get('/cordova_plugins.js', servePlatformResource);
218234
app.get('/plugins/*', servePlatformResource);
219235

220-
if (options.proxy) {
221-
await setupProxies(env, app);
236+
const livereloadUrl = `http://localhost:${options.livereloadPort}`;
237+
const pathPrefix = `/${DEV_SERVER_PREFIX}/tiny-lr`;
238+
const proxyMiddlewareCfg: proxyMiddlewareType.Config = { changeOrigin: true, ws: true, logLevel: 'warn', logProvider: () => env.log };
239+
240+
app.use(pathPrefix, proxyMiddleware(pathPrefix, { target: livereloadUrl, pathRewrite: { [pathPrefix]: '' }, ...proxyMiddlewareCfg }));
241+
242+
if (options.proxy && project.proxies) {
243+
for (const proxy of project.proxies) {
244+
const opts = { target: proxy.proxyUrl, pathRewrite: { [proxy.path]: '' }, ...proxyMiddlewareCfg };
245+
246+
if (proxy.proxyNoAgent) {
247+
opts.agent = <any>false; // TODO: type issue
248+
}
249+
250+
if (proxy.rejectUnauthorized === false) {
251+
opts.secure = false;
252+
}
253+
254+
app.use(proxy.path, proxyMiddleware(proxy.path, opts));
255+
env.log.info(`Proxy created ${chalk.bold(proxy.path)} => ${chalk.bold(opts.target)}`);
256+
}
222257
}
223258

224259
app.get(`/${DEV_SERVER_PREFIX}/dev-server.js`, async (req, res) => {
@@ -288,24 +323,6 @@ async function createHttpServer(env: IonicEnvironment, options: ServeMetaOptions
288323
});
289324
}
290325

291-
async function setupProxies(env: IonicEnvironment, app: expressType.Application) {
292-
const url = await import('url');
293-
const project = await env.project.load();
294-
295-
for (const proxy of project.proxies || []) {
296-
const opts: any = url.parse(proxy.proxyUrl);
297-
if (proxy.proxyNoAgent) {
298-
opts.agent = false;
299-
}
300-
301-
opts.rejectUnauthorized = !(proxy.rejectUnauthorized === false);
302-
303-
const proxyMiddleware = await import('proxy-middleware');
304-
app.use(proxy.path, <expressType.RequestHandler>proxyMiddleware(opts));
305-
console.log('Proxy added:' + proxy.path + ' => ' + url.format(opts));
306-
}
307-
}
308-
309326
/**
310327
* http responder for cordova.js file
311328
*/

packages/@ionic/cli-utils/src/lib/utils/logger.ts

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as util from 'util';
33
import chalk from 'chalk';
44
import { Chalk } from 'chalk';
55

6-
import { ILogger, LogLevel, LogPrefix, LoggerOptions } from '../../definitions';
6+
import { ILogger, LogLevel, LogMsg, LogPrefix, LoggerOptions } from '../../definitions';
77
import { LOG_LEVELS } from '../../guards';
88
import { wordWrap } from './format';
99

@@ -30,38 +30,42 @@ export class Logger implements ILogger {
3030
this.stream = stream;
3131
}
3232

33-
debug(msg: string | (() => string)): void {
34-
this.log('debug', msg);
33+
debug(msg: LogMsg): void {
34+
this._log('debug', msg);
3535
}
3636

37-
info(msg: string | (() => string)): void {
38-
this.log('info', msg);
37+
info(msg: LogMsg): void {
38+
this._log('info', msg);
3939
}
4040

41-
ok(msg: string | (() => string)): void {
42-
this.log('ok', msg);
41+
ok(msg: LogMsg): void {
42+
this._log('ok', msg);
4343
}
4444

45-
warn(msg: string | (() => string)): void {
46-
this.log('warn', msg);
45+
warn(msg: LogMsg): void {
46+
this._log('warn', msg);
4747
}
4848

49-
error(msg: string | (() => string)): void {
50-
this.log('error', msg);
49+
error(msg: LogMsg): void {
50+
this._log('error', msg);
5151
}
5252

53-
announce(msg: string | (() => string)): void {
54-
this.log('announce', msg);
53+
announce(msg: LogMsg): void {
54+
this._log('announce', msg);
5555
}
5656

57-
msg(msg: string | (() => string)): void {
57+
msg(msg: LogMsg): void {
5858
if (typeof msg === 'function') {
5959
msg = msg();
6060
}
6161

6262
this.stream.write(this.enforceLF(msg));
6363
}
6464

65+
log(msg: LogMsg): void {
66+
this.msg(msg);
67+
}
68+
6569
nl(num: number = 1): void {
6670
this.stream.write(this.enforceLF('\n'.repeat(num)));
6771
}
@@ -84,7 +88,7 @@ export class Logger implements ILogger {
8488
return color;
8589
}
8690

87-
private log(level: LogLevel, msg: string | (() => string)): void {
91+
private _log(level: LogLevel, msg: LogMsg): void {
8892
if (this.shouldLog(level)) {
8993
let prefix = this.prefix;
9094

packages/@ionic/cli-utils/tsconfig.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
"../../../types/elementtree.d.ts",
3232
"../../../types/leek.d.ts",
3333
"../../../types/os-name.d.ts",
34-
"../../../types/proxy-middleware.d.ts",
3534
"../../../types/slice-ansi.d.ts",
3635
"../../../types/ssh-config.d.ts",
3736
"../../../types/string-width.d.ts",

types/proxy-middleware.d.ts

Lines changed: 0 additions & 6 deletions
This file was deleted.

0 commit comments

Comments
 (0)