From 89fb9c3fa05f345e17a5bc0b5032bb8301861f06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jordi=20Guti=C3=A9rrez=20Hermoso?= Date: Thu, 9 May 2024 16:43:01 -0400 Subject: [PATCH 1/2] debugging: add a separate start:debug-brk target --inspect-brk is useful if you want to debug something during startup --- package.json | 3 ++- sandbox/watch.sh | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) 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 From 0c9bed9de57069572c8dd979d7f36d36c3014075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jordi=20Guti=C3=A9rrez=20Hermoso?= Date: Thu, 9 May 2024 16:46:25 -0400 Subject: [PATCH 2/2] BootProbes: add a websocket probe This self-test isn't perfect because we're running it from the backend instead of the frontend. Conceivably the backend might have trouble resolving its own url. Eventually we should move this test or something like it to something that executes in the frontend. --- app/client/models/AdminChecks.ts | 10 ++++++++++ app/common/BootProbe.ts | 3 ++- app/server/lib/BootProbes.ts | 33 ++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/app/client/models/AdminChecks.ts b/app/client/models/AdminChecks.ts index da720306..1ad4c87b 100644 --- a/app/client/models/AdminChecks.ts +++ b/app/client/models/AdminChecks.ts @@ -169,6 +169,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 242ab0d8..3d6783f3 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',