diff --git a/app/client/models/AdminChecks.ts b/app/client/models/AdminChecks.ts
index 40baf812..96a6f8d7 100644
--- a/app/client/models/AdminChecks.ts
+++ b/app/client/models/AdminChecks.ts
@@ -162,6 +162,16 @@ It is good practice not to run Grist as the root user.
'reachable': {
info: `
The main page of Grist should be available.
+`
+ },
+
+ 'websockets': {
+ info: `
+Websocket connections need HTTP 1.1 and the ability to pass a few
+extra headers in order to work. Sometimes a reverse proxy can
+interfere with these requirements. See this
+documentation for more explanation.
`
},
};
diff --git a/app/common/BootProbe.ts b/app/common/BootProbe.ts
index 753af1c4..699d3515 100644
--- a/app/common/BootProbe.ts
+++ b/app/common/BootProbe.ts
@@ -6,7 +6,8 @@ export type BootProbeIds =
'reachable' |
'host-header' |
'sandboxing' |
- 'system-user'
+ 'system-user' |
+ 'websockets'
;
export interface BootProbeResult {
diff --git a/app/server/lib/BootProbes.ts b/app/server/lib/BootProbes.ts
index 6c21c8a0..3dd02bf9 100644
--- a/app/server/lib/BootProbes.ts
+++ b/app/server/lib/BootProbes.ts
@@ -4,6 +4,7 @@ import { removeTrailingSlash } from 'app/common/gutil';
import { expressWrap, jsonErrorHandler } from 'app/server/lib/expressWrap';
import { GristServer } from 'app/server/lib/GristServer';
import * as express from 'express';
+import WS from 'ws';
import fetch from 'node-fetch';
/**
@@ -58,6 +59,7 @@ export class BootProbes {
this._probes.push(_bootProbe);
this._probes.push(_hostHeaderProbe);
this._probes.push(_sandboxingProbe);
+ this._probes.push(_webSocketsProbe);
this._probeById = new Map(this._probes.map(p => [p.id, p]));
}
}
@@ -104,6 +106,37 @@ const _homeUrlReachableProbe: Probe = {
}
};
+const _webSocketsProbe: Probe = {
+ id: 'websockets',
+ name: 'Can we open a websocket with the server',
+ apply: async (server, req) => {
+ return new Promise((resolve) => {
+ const url = new URL(server.getHomeUrl(req));
+ url.protocol = (url.protocol === 'https:') ? 'wss:' : 'ws:';
+ const ws = new WS.WebSocket(url.href);
+ const details: Record = {
+ url,
+ };
+ ws.on('open', () => {
+ ws.send('Just nod if you can hear me.');
+ resolve({
+ success: true,
+ details,
+ });
+ ws.close();
+ });
+ ws.on('error', (ev) => {
+ details.error = ev.message;
+ resolve({
+ success: false,
+ details,
+ });
+ ws.close();
+ });
+ });
+ }
+};
+
const _statusCheckProbe: Probe = {
id: 'health-check',
name: 'Is an internal health check passing',
diff --git a/package.json b/package.json
index 44b167b0..73a8b7a9 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,8 @@
"repository": "git://github.com/gristlabs/grist-core.git",
"scripts": {
"start": "sandbox/watch.sh",
- "start:debug": "NODE_INSPECT=1 sandbox/watch.sh",
+ "start:debug": "NODE_INSPECT=--inspect sandbox/watch.sh",
+ "start:debug-brk": "NODE_INSPECT=--inspect-brk sandbox/watch.sh",
"install:python": "buildtools/prepare_python.sh",
"install:python2": "buildtools/prepare_python2.sh",
"install:python3": "buildtools/prepare_python3.sh",
diff --git a/sandbox/watch.sh b/sandbox/watch.sh
index 6cb7a750..531c97f6 100755
--- a/sandbox/watch.sh
+++ b/sandbox/watch.sh
@@ -19,6 +19,6 @@ tsc --build -w --preserveWatchOutput $PROJECT &
css_files="app/client/**/*.css"
chokidar "${css_files}" -c "bash -O globstar -c 'cat ${css_files} > static/bundle.css'" &
webpack --config $WEBPACK_CONFIG --mode development --watch &
-NODE_PATH=_build:_build/stubs:_build/ext nodemon ${NODE_INSPECT:+--inspect} --delay 1 -w _build/app/server -w _build/app/common _build/stubs/app/server/server.js &
+NODE_PATH=_build:_build/stubs:_build/ext nodemon ${NODE_INSPECT} --delay 1 -w _build/app/server -w _build/app/common _build/stubs/app/server/server.js &
wait