2017-12-18 02:22:09 +00:00
|
|
|
import * as dns from "dns-then";
|
|
|
|
import { LogService } from "matrix-js-snippets";
|
2017-12-29 01:22:50 +00:00
|
|
|
import { Cache, CACHE_FEDERATION } from "../MemoryCache";
|
2017-12-18 02:22:09 +00:00
|
|
|
import * as request from "request";
|
2018-03-24 23:09:34 +00:00
|
|
|
import config from "../config";
|
2017-12-18 02:22:09 +00:00
|
|
|
|
2018-03-24 03:26:14 +00:00
|
|
|
export async function getFederationUrl(serverName: string): Promise<string> {
|
2017-12-29 01:22:50 +00:00
|
|
|
const cachedUrl = Cache.for(CACHE_FEDERATION).get(serverName);
|
2017-12-18 02:22:09 +00:00
|
|
|
if (cachedUrl) {
|
|
|
|
LogService.verbose("matrix", "Cached federation URL for " + serverName + " is " + cachedUrl);
|
2018-03-24 03:26:14 +00:00
|
|
|
return cachedUrl;
|
2017-12-18 02:22:09 +00:00
|
|
|
}
|
|
|
|
|
2018-03-24 23:09:34 +00:00
|
|
|
if (serverName === config.homeserver.name && config.homeserver.federationUrl) {
|
|
|
|
let url = config.homeserver.federationUrl;
|
|
|
|
if (url.endsWith("/")) {
|
|
|
|
url = url.substring(0, url.length - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
LogService.info("matrix", "Using configured federation URL for " + serverName);
|
|
|
|
Cache.for(CACHE_FEDERATION).put(serverName, url);
|
|
|
|
return url;
|
|
|
|
}
|
|
|
|
|
2017-12-18 02:22:09 +00:00
|
|
|
let serverUrl = null;
|
|
|
|
let expirationMs = 4 * 60 * 60 * 1000; // default is 4 hours
|
2018-03-24 03:26:14 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
const records = await dns.resolveSrv("_matrix._tcp." + serverName);
|
2017-12-18 02:22:09 +00:00
|
|
|
if (records && records.length > 0) {
|
|
|
|
serverUrl = "https://" + records[0].name + ":" + records[0].port;
|
|
|
|
expirationMs = records[0].ttl * 1000;
|
|
|
|
}
|
2018-03-24 03:26:14 +00:00
|
|
|
} catch (err) {
|
2017-12-18 02:22:09 +00:00
|
|
|
// Not having the SRV record isn't bad, it just means that the server operator decided to not use SRV records.
|
|
|
|
// When there's no SRV record we default to port 8448 (as per the federation rules) in the lower .then()
|
|
|
|
// People tend to think that the lack of an SRV record is bad, but in reality it's only a problem if one was set and
|
|
|
|
// it's not being found. Most people don't set up the SRV record, but some do.
|
2018-03-24 03:26:14 +00:00
|
|
|
LogService.verbose("matrix", err);
|
2017-12-18 02:22:09 +00:00
|
|
|
LogService.warn("matrix", "Could not find _matrix._tcp." + serverName + " DNS record. This is normal for most servers.");
|
2018-03-24 03:26:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!serverUrl) serverUrl = "https://" + serverName + ":8448";
|
|
|
|
LogService.verbose("matrix", "Federation URL for " + serverName + " is " + serverUrl + " - caching for " + expirationMs + " ms");
|
|
|
|
Cache.for(CACHE_FEDERATION).put(serverName, serverUrl, expirationMs);
|
|
|
|
return serverUrl;
|
2017-12-18 02:22:09 +00:00
|
|
|
}
|
|
|
|
|
2018-03-24 03:26:14 +00:00
|
|
|
export async function doFederatedApiCall(method: string, serverName: string, endpoint: string, query?: object, body?: object): Promise<any> {
|
|
|
|
const federationUrl = await getFederationUrl(serverName);
|
2018-03-30 22:34:39 +00:00
|
|
|
LogService.info("matrix", "Doing federated API call: " + federationUrl + endpoint);
|
2018-03-24 03:26:14 +00:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
request({
|
|
|
|
method: method,
|
|
|
|
url: federationUrl + endpoint,
|
|
|
|
qs: query,
|
|
|
|
json: body,
|
|
|
|
rejectUnauthorized: false, // allow self signed certs (for federation)
|
|
|
|
}, (err, res, _body) => {
|
|
|
|
if (err) {
|
|
|
|
LogService.error("matrix", "Error calling " + endpoint);
|
|
|
|
LogService.error("matrix", err);
|
|
|
|
reject(err);
|
|
|
|
} else if (res.statusCode !== 200) {
|
2018-03-24 23:09:34 +00:00
|
|
|
LogService.error("matrix", "Got status code " + res.statusCode + " while calling federated endpoint " + endpoint);
|
|
|
|
reject(new Error("Error in request: invalid status code"));
|
|
|
|
} else {
|
|
|
|
if (typeof(res.body) === "string") res.body = JSON.parse(res.body);
|
|
|
|
resolve(res.body);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function doClientApiCall(method: string, endpoint: string, query?: object, body?: object): Promise<any> {
|
|
|
|
let url = config.homeserver.clientServerUrl;
|
|
|
|
if (url.endsWith("/")) url = url.substring(0, url.length - 1);
|
2018-03-30 22:34:39 +00:00
|
|
|
LogService.info("matrix", "Doing client API call: " + url + endpoint);
|
2018-03-24 23:09:34 +00:00
|
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
request({
|
|
|
|
method: method,
|
|
|
|
url: url + endpoint,
|
|
|
|
qs: query,
|
|
|
|
json: body,
|
|
|
|
}, (err, res, _body) => {
|
|
|
|
if (err) {
|
|
|
|
LogService.error("matrix", "Error calling " + endpoint);
|
|
|
|
LogService.error("matrix", err);
|
|
|
|
reject(err);
|
|
|
|
} else if (res.statusCode !== 200) {
|
|
|
|
LogService.error("matrix", "Got status code " + res.statusCode + " while calling client endpoint " + endpoint);
|
2018-03-24 03:26:14 +00:00
|
|
|
reject(new Error("Error in request: invalid status code"));
|
|
|
|
} else {
|
|
|
|
if (typeof(res.body) === "string") res.body = JSON.parse(res.body);
|
|
|
|
resolve(res.body);
|
|
|
|
}
|
2017-12-18 02:22:09 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|