Skip to content

Commit 6c789ea

Browse files
Do our best to get the user to the right place and try joining via derived server name (#168)
Split out from #167
1 parent e99a0d6 commit 6c789ea

File tree

5 files changed

+143
-6
lines changed

5 files changed

+143
-6
lines changed

server/lib/matrix-utils/ensure-room-joined.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,29 @@ const assert = require('assert');
44
const urlJoin = require('url-join');
55

66
const { fetchEndpointAsJson } = require('../fetch-endpoint');
7+
const getServerNameFromMatrixRoomIdOrAlias = require('./get-server-name-from-matrix-room-id-or-alias');
78

89
const config = require('../config');
910
const StatusError = require('../status-error');
1011
const matrixServerUrl = config.get('matrixServerUrl');
1112
assert(matrixServerUrl);
1213

13-
async function ensureRoomJoined(accessToken, roomIdOrAlias, viaServers = []) {
14+
async function ensureRoomJoined(accessToken, roomIdOrAlias, viaServers = new Set()) {
15+
// We use a `Set` to ensure that we don't have duplicate servers in the list
16+
assert(viaServers instanceof Set);
17+
18+
// Let's do our best for the user to join the room. Since room ID's are
19+
// unroutable on their own and won't be found if the server doesn't already
20+
// know about the room, we'll try to join the room via the server name that
21+
// we derived from the room ID or alias.
22+
const viaServersWithAssumptions = new Set(viaServers);
23+
const derivedServerName = getServerNameFromMatrixRoomIdOrAlias(roomIdOrAlias);
24+
if (derivedServerName) {
25+
viaServersWithAssumptions.add(derivedServerName);
26+
}
27+
1428
let qs = new URLSearchParams();
15-
[].concat(viaServers).forEach((viaServer) => {
29+
Array.from(viaServersWithAssumptions).forEach((viaServer) => {
1630
qs.append('server_name', viaServer);
1731
});
1832

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use strict';
2+
3+
const assert = require('assert');
4+
5+
// See https://spec.matrix.org/v1.5/appendices/#server-name
6+
function getServerNameFromMatrixRoomIdOrAlias(roomIdOrAlias) {
7+
assert(roomIdOrAlias);
8+
9+
const pieces = roomIdOrAlias.split(':');
10+
// We can only derive the server name if there is a colon in the string. Since room
11+
// IDs are supposed to be treated as opaque strings, there is a future possibility
12+
// that they will not contain a colon.
13+
if (pieces.length < 2) {
14+
return null;
15+
}
16+
17+
const servername = pieces.slice(1).join(':');
18+
19+
return servername;
20+
}
21+
22+
module.exports = getServerNameFromMatrixRoomIdOrAlias;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use strict';
2+
3+
const StatusError = require('../lib/status-error');
4+
5+
function parseViaServersFromUserInput(rawViaServers) {
6+
// `rawViaServers` could be an array, a single string, or undefined. Turn it into an
7+
// array no matter what
8+
const rawViaServerList = [].concat(rawViaServers || []);
9+
if (rawViaServerList.length === 0) {
10+
return new Set();
11+
}
12+
13+
const viaServerList = rawViaServerList.map((viaServer) => {
14+
// Sanity check to ensure that the via servers are strings (valid enough looking
15+
// host names)
16+
if (typeof viaServer !== 'string') {
17+
throw new StatusError(
18+
400,
19+
`?via server must be a string, got ${viaServer} (${typeof viaServer})`
20+
);
21+
}
22+
23+
return viaServer;
24+
});
25+
26+
// We use a `Set` to ensure that we don't have duplicate servers in the list
27+
return new Set(viaServerList);
28+
}
29+
30+
module.exports = parseViaServersFromUserInput;

server/routes/room-routes.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const timeoutMiddleware = require('./timeout-middleware');
1111
const redirectToCorrectArchiveUrlIfBadSigil = require('./redirect-to-correct-archive-url-if-bad-sigil-middleware');
1212

1313
const { HTTPResponseError } = require('../lib/fetch-endpoint');
14+
const parseViaServersFromUserInput = require('../lib/parse-via-servers-from-user-input');
1415
const fetchRoomData = require('../lib/matrix-utils/fetch-room-data');
1516
const fetchEventsFromTimestampBackwards = require('../lib/matrix-utils/fetch-events-from-timestamp-backwards');
1617
const ensureRoomJoined = require('../lib/matrix-utils/ensure-room-joined');
@@ -173,7 +174,11 @@ router.get(
173174

174175
// We have to wait for the room join to happen first before we can fetch
175176
// any of the additional room info or messages.
176-
const roomId = await ensureRoomJoined(matrixAccessToken, roomIdOrAlias, req.query.via);
177+
const roomId = await ensureRoomJoined(
178+
matrixAccessToken,
179+
roomIdOrAlias,
180+
parseViaServersFromUserInput(req.query.via)
181+
);
177182

178183
// Find the closest day to the current time with messages
179184
const { originServerTs } = await timestampToEvent({
@@ -192,7 +197,7 @@ router.get(
192197
// We can avoid passing along the `via` query parameter because we already
193198
// joined the room above (see `ensureRoomJoined`).
194199
//
195-
//viaServers: req.query.via,
200+
//viaServers: parseViaServersFromUserInput(req.query.via),
196201
})
197202
);
198203
})
@@ -245,7 +250,11 @@ router.get(
245250

246251
// We have to wait for the room join to happen first before we can use the jump to
247252
// date endpoint
248-
const roomId = await ensureRoomJoined(matrixAccessToken, roomIdOrAlias, req.query.via);
253+
const roomId = await ensureRoomJoined(
254+
matrixAccessToken,
255+
roomIdOrAlias,
256+
parseViaServersFromUserInput(req.query.via)
257+
);
249258

250259
let eventIdForClosestEvent;
251260
let tsForClosestEvent;
@@ -536,7 +545,11 @@ router.get(
536545

537546
// We have to wait for the room join to happen first before we can fetch
538547
// any of the additional room info or messages.
539-
const roomId = await ensureRoomJoined(matrixAccessToken, roomIdOrAlias, req.query.via);
548+
const roomId = await ensureRoomJoined(
549+
matrixAccessToken,
550+
roomIdOrAlias,
551+
parseViaServersFromUserInput(req.query.via)
552+
);
540553

541554
// Do these in parallel to avoid the extra time in sequential round-trips
542555
// (we want to display the archive page faster)
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
'use strict';
2+
3+
const assert = require('assert');
4+
5+
const getServerNameFromMatrixRoomIdOrAlias = require('../../../server/lib/matrix-utils/get-server-name-from-matrix-room-id-or-alias');
6+
7+
describe('getServerNameFromMatrixRoomIdOrAlias', () => {
8+
// Some examples from https://spec.matrix.org/v1.5/appendices/#server-name
9+
const testCases = [
10+
{
11+
name: 'can parse normal looking domain name',
12+
input: '!foo:matrix.org',
13+
expected: 'matrix.org',
14+
},
15+
{
16+
name: 'can parse sub-domain',
17+
input: '!foo:archive.matrix.org',
18+
expected: 'archive.matrix.org',
19+
},
20+
{
21+
name: 'can parse domain with port',
22+
input: '!foo:matrix.org:8888',
23+
expected: 'matrix.org:8888',
24+
},
25+
{
26+
name: 'can parse IPv4 address',
27+
input: '!foo:192.168.1.1',
28+
expected: '192.168.1.1',
29+
},
30+
{
31+
name: 'can parse IPv4 address with port',
32+
input: '!foo:192.168.1.1:1234',
33+
expected: '192.168.1.1:1234',
34+
},
35+
{
36+
name: 'can parse IPv6 address',
37+
input: '!foo:[1234:5678::abcd]',
38+
expected: '[1234:5678::abcd]',
39+
},
40+
{
41+
name: 'can parse IPv6 address with port',
42+
input: '!foo:[1234:5678::abcd]:1234',
43+
expected: '[1234:5678::abcd]:1234',
44+
},
45+
{
46+
name: `opaque room ID is *NOT* parsed and we can't derive a server name`,
47+
input: '!foobarbaz',
48+
expected: null,
49+
},
50+
];
51+
52+
testCases.forEach((testCaseMeta) => {
53+
it(testCaseMeta.name, () => {
54+
const actual = getServerNameFromMatrixRoomIdOrAlias(testCaseMeta.input);
55+
assert.strictEqual(actual, testCaseMeta.expected);
56+
});
57+
});
58+
});

0 commit comments

Comments
 (0)