Skip to content

Commit 7e25548

Browse files
alan-agius4vikerman
authored andcommitted
fix(builders): allow setting a public host (#1401)
Closes #1394 and closes #1393
1 parent b7c5cd8 commit 7e25548

File tree

7 files changed

+121
-26
lines changed

7 files changed

+121
-26
lines changed

modules/builders/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ ts_library(
3333
"@npm//@angular-devkit/architect",
3434
"@npm//@angular-devkit/core",
3535
"@npm//@types/browser-sync",
36+
"@npm//@types/http-proxy-middleware",
3637
"@npm//browser-sync",
38+
"@npm//http-proxy-middleware",
3739
"@npm//rxjs",
3840
"@npm//tree-kill",
3941
],

modules/builders/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"@angular-devkit/architect": "DEVKIT_ARCHITECT_VERSION",
1818
"@angular-devkit/core": "DEVKIT_CORE_VERSION",
1919
"browser-sync": "^2.26.7",
20+
"http-proxy-middleware": "^0.20.0",
2021
"rxjs": "RXJS_VERSION",
2122
"tree-kill": "^1.2.1"
2223
},

modules/builders/src/ssr-dev-server/index.ts

Lines changed: 69 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ import {
3838
} from 'rxjs/operators';
3939
import * as browserSync from 'browser-sync';
4040
import { join } from 'path';
41+
import * as url from 'url';
42+
import * as proxy from 'http-proxy-middleware';
4143

4244
import { getAvailablePort, spawnAsObservable, waitUntilServerIsListening } from './utils';
4345

@@ -201,31 +203,75 @@ async function initBrowserSync(
201203
return browserSyncInstance;
202204
}
203205

204-
const { port, open, host } = options;
205-
const bsPort = port || await getAvailablePort();
206+
const { port: browserSyncPort, open, host, publicHost } = options;
207+
const bsPort = browserSyncPort || await getAvailablePort();
208+
const bsOptions: browserSync.Options = {
209+
proxy: {
210+
target: `localhost:${nodeServerPort}`,
211+
proxyRes: [
212+
proxyRes => {
213+
if ('headers' in proxyRes) {
214+
proxyRes.headers['cache-control'] = undefined;
215+
}
216+
},
217+
]
218+
},
219+
host,
220+
port: bsPort,
221+
ui: false,
222+
server: false,
223+
notify: false,
224+
ghostMode: false,
225+
logLevel: 'silent',
226+
open,
227+
// Remove leading slash
228+
scriptPath: path => path.substring(1),
229+
};
230+
231+
const publicHostNormalized = publicHost && publicHost.endsWith('/')
232+
? publicHost.substring(0, publicHost.length - 1)
233+
: publicHost;
234+
235+
if (publicHostNormalized) {
236+
const { protocol, hostname, port, pathname } = url.parse(publicHostNormalized);
237+
const defaultSocketIoPath = '/browser-sync/socket.io';
238+
const defaultNamespace = '/browser-sync';
239+
const hasPathname = !!(pathname && pathname !== '/');
240+
const namespace = hasPathname ? pathname + defaultNamespace : defaultNamespace;
241+
const path = hasPathname ? pathname + defaultSocketIoPath : defaultSocketIoPath;
242+
243+
bsOptions.socket = {
244+
namespace,
245+
path,
246+
domain: url.format({
247+
protocol,
248+
hostname,
249+
port,
250+
}),
251+
};
252+
253+
// When having a pathname we also need to create a reverse proxy because socket.io
254+
// will be listening on: 'http://localhost:4200/ssr/browser-sync/socket.io'
255+
// However users will typically have a reverse proxy that will redirect all matching requests
256+
// ex: http://testinghost.com/ssr -> http://localhost:4200 which will result in a 404.
257+
if (hasPathname) {
258+
bsOptions.middleware = [
259+
proxy(defaultSocketIoPath, {
260+
target: url.format({
261+
protocol: 'http',
262+
hostname: host,
263+
port: bsPort,
264+
pathname: path,
265+
}),
266+
ws: true,
267+
logLevel: 'silent',
268+
}),
269+
];
270+
}
271+
}
206272

207273
return new Promise((resolve, reject) => {
208-
browserSyncInstance
209-
.init({
210-
proxy: {
211-
target: `localhost:${nodeServerPort}`,
212-
proxyRes: [
213-
proxyRes => {
214-
if ('headers' in proxyRes) {
215-
proxyRes.headers['cache-control'] = undefined;
216-
}
217-
},
218-
]
219-
},
220-
host,
221-
port: bsPort,
222-
ui: false,
223-
server: false,
224-
notify: false,
225-
ghostMode: false,
226-
logLevel: 'silent',
227-
open,
228-
}, (error, bs) => {
274+
browserSyncInstance.init(bsOptions, (error, bs) => {
229275
if (error) {
230276
reject(error);
231277
} else {

modules/builders/src/ssr-dev-server/schema.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
"default": 4200,
2525
"description": "Port to start the development server at. Default is 4200. Pass 0 to get a dynamically assigned port."
2626
},
27+
"publicHost": {
28+
"type": "string",
29+
"description": "The URL that the browser client should use to connect to the development server. Use for a complex dev server setup, such as one with reverse proxies."
30+
},
2731
"open": {
2832
"type": "boolean",
2933
"description": "Opens the url in default browser.",

modules/builders/src/ssr-dev-server/schema.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,10 @@ export interface Schema {
2727

2828
/** Opens the url in default browser. */
2929
open?: boolean;
30+
31+
/**
32+
* The URL that the browser client should use to connect to the development server.
33+
* Use for a complex dev server setup, such as one with reverse proxies.
34+
*/
35+
publicHost?: string;
3036
}

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@
3737
},
3838
"devDependencies": {
3939
"@angular-devkit/architect": "^0.900.0-rc.6",
40-
"@angular/cli": "^9.0.0-rc.6",
4140
"@angular-devkit/build-angular": "^0.900.0-rc.6",
4241
"@angular-devkit/core": "^9.0.0-rc.6",
4342
"@angular-devkit/schematics": "^9.0.0-rc.6",
4443
"@angular/animations": "^9.0.0-rc.6",
4544
"@angular/bazel": "^9.0.0-rc.6",
45+
"@angular/cli": "^9.0.0-rc.6",
4646
"@angular/common": "^9.0.0-rc.6",
4747
"@angular/compiler": "^9.0.0-rc.6",
4848
"@angular/compiler-cli": "^9.0.0-rc.6",
@@ -65,12 +65,14 @@
6565
"@types/fs-extra": "^8.0.0",
6666
"@types/hapi__hapi": "^18.2.5",
6767
"@types/hapi__inert": "^5.2.0",
68+
"@types/http-proxy-middleware": "^0.19.3",
6869
"@types/jasmine": "^3.4.4",
6970
"@types/node": "^12.11.1",
7071
"@types/shelljs": "^0.8.6",
7172
"browser-sync": "^2.26.7",
7273
"domino": "^2.1.2",
7374
"express": "^4.15.2",
75+
"http-proxy-middleware": "^0.20.0",
7476
"jasmine-core": "^3.0.0",
7577
"karma": "^4.1.0",
7678
"karma-chrome-launcher": "^3.0.0",

yarn.lock

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1508,6 +1508,22 @@
15081508
dependencies:
15091509
"@types/node" "*"
15101510

1511+
"@types/http-proxy-middleware@^0.19.3":
1512+
version "0.19.3"
1513+
resolved "https://registry.yarnpkg.com/@types/http-proxy-middleware/-/http-proxy-middleware-0.19.3.tgz#b2eb96fbc0f9ac7250b5d9c4c53aade049497d03"
1514+
integrity sha512-lnBTx6HCOUeIJMLbI/LaL5EmdKLhczJY5oeXZpX/cXE4rRqb3RmV7VcMpiEfYkmTjipv3h7IAyIINe4plEv7cA==
1515+
dependencies:
1516+
"@types/connect" "*"
1517+
"@types/http-proxy" "*"
1518+
"@types/node" "*"
1519+
1520+
"@types/http-proxy@*":
1521+
version "1.17.2"
1522+
resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.2.tgz#3b7fb5365a00d47129967b0b2da51c2123692314"
1523+
integrity sha512-Qfb7batJJBlI8wcrd48vHpgsOOYzQQa+OZcaIz33jkJPe8A7KktAJFmRAiR42s5BfnErdlFnOyQucq2BKy/98g==
1524+
dependencies:
1525+
"@types/node" "*"
1526+
15111527
"@types/jasmine@^3.4.4":
15121528
version "3.5.0"
15131529
resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.5.0.tgz#2ad2006c8a937d20df20a8fee86071d0f730ef99"
@@ -2415,7 +2431,7 @@ braces@^2.3.1, braces@^2.3.2:
24152431
split-string "^3.0.2"
24162432
to-regex "^3.0.1"
24172433

2418-
braces@^3.0.2, braces@~3.0.2:
2434+
braces@^3.0.1, braces@^3.0.2, braces@~3.0.2:
24192435
version "3.0.2"
24202436
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
24212437
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
@@ -4732,6 +4748,16 @@ http-proxy-middleware@0.19.1:
47324748
lodash "^4.17.11"
47334749
micromatch "^3.1.10"
47344750

4751+
http-proxy-middleware@^0.20.0:
4752+
version "0.20.0"
4753+
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.20.0.tgz#5b128f7207985c4ea91b53fab8ad897a48c690d6"
4754+
integrity sha512-dNJAk71nEJhPiAczQH9hGvE/MT9kEs+zn2Dh+Hi94PGZe1GluQirC7mw5rdREUtWx6qGS1Gu0bZd4qEAg+REgw==
4755+
dependencies:
4756+
http-proxy "^1.17.0"
4757+
is-glob "^4.0.1"
4758+
lodash "^4.17.14"
4759+
micromatch "^4.0.2"
4760+
47354761
http-proxy@1.15.2:
47364762
version "1.15.2"
47374763
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.15.2.tgz#642fdcaffe52d3448d2bda3b0079e9409064da31"
@@ -6097,6 +6123,14 @@ micromatch@^3.1.10, micromatch@^3.1.4:
60976123
snapdragon "^0.8.1"
60986124
to-regex "^3.0.2"
60996125

6126+
micromatch@^4.0.2:
6127+
version "4.0.2"
6128+
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259"
6129+
integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==
6130+
dependencies:
6131+
braces "^3.0.1"
6132+
picomatch "^2.0.5"
6133+
61006134
miller-rabin@^4.0.0:
61016135
version "4.0.1"
61026136
resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
@@ -7020,7 +7054,7 @@ performance-now@^2.1.0:
70207054
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
70217055
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
70227056

7023-
picomatch@^2.0.4, picomatch@^2.0.7:
7057+
picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.0.7:
70247058
version "2.1.1"
70257059
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.1.1.tgz#ecdfbea7704adb5fe6fb47f9866c4c0e15e905c5"
70267060
integrity sha512-OYMyqkKzK7blWO/+XZYP6w8hH0LDvkBvdvKukti+7kqYFCiEAk+gI3DWnryapc0Dau05ugGTy0foQ6mqn4AHYA==

0 commit comments

Comments
 (0)