mirror of
https://github.com/jambonz/sbc-sip-sidecar.git
synced 2026-08-19 09:40:25 +00:00
add server discovery to options (#142)
This commit is contained in:
@@ -28,6 +28,25 @@ Configuration is provided via environment variables:
|
||||
|ENCRYPTION_SECRET| secret for credential encryption(JWT_SECRET is deprecated) |yes|
|
||||
|JAMBONES_REGBOT_DEFAULT_EXPIRES_INTERVAL| default expire value for outbound registration in seconds (default 3600) |no|
|
||||
|JAMBONES_REGBOT_MIN_EXPIRES_INTERVAL| minimum expire value for outbound registration in seconds (default 30) |no|
|
||||
|JAMBONES_SERVER_CONTROL| set to a truthy value ('1', 'true', 'yes') to enable server-control features such as topology discovery via OPTIONS (see below) |no|
|
||||
|
||||
## Server control
|
||||
|
||||
When `JAMBONES_SERVER_CONTROL` is enabled, the SBC exposes additional server-control features.
|
||||
|
||||
### Topology discovery
|
||||
|
||||
An OPTIONS request carrying the header `X-Jambonz-Discover: true` (from any IP, including external ones) is answered with a `200 OK` whose JSON body lists the current cluster topology read from redis:
|
||||
|
||||
```json
|
||||
{
|
||||
"featureServers": ["10.0.0.10:5060"],
|
||||
"sipServers": ["1.2.3.4"],
|
||||
"rtpServers": ["10.0.0.20"]
|
||||
}
|
||||
```
|
||||
|
||||
`featureServers`, `sipServers` and `rtpServers` are the IPs (feature servers include the port) of the active feature servers, SIP servers and RTP servers respectively. When `JAMBONES_SERVER_CONTROL` is not set, the discovery header is ignored and OPTIONS behaves as normal.
|
||||
|
||||
## CLI Management
|
||||
|
||||
|
||||
+5
-1
@@ -45,6 +45,9 @@ const JAMBONES_REGBOT_USER_AGENT = process.env.JAMBONES_REGBOT_USER_AGENT ;
|
||||
const JAMBONES_REGBOT_FAILURE_RETRY_INTERVAL = process.env.JAMBONES_REGBOT_FAILURE_RETRY_INTERVAL;
|
||||
const JAMBONES_REGBOT_REGISTER_FAILURE_THRESHOLD = process.env.JAMBONES_REGBOT_REGISTER_FAILURE_THRESHOLD;
|
||||
|
||||
/* Server control - external topology discovery and other server-control features (disabled unless truthy) */
|
||||
const JAMBONES_SERVER_CONTROL = process.env.JAMBONES_SERVER_CONTROL;
|
||||
|
||||
module.exports = {
|
||||
JAMBONES_MYSQL_HOST,
|
||||
JAMBONES_MYSQL_USER,
|
||||
@@ -81,5 +84,6 @@ module.exports = {
|
||||
REGISTER_RESPONSE_REMOVE,
|
||||
JAMBONES_REGBOT_USER_AGENT,
|
||||
JAMBONES_REGBOT_FAILURE_RETRY_INTERVAL,
|
||||
JAMBONES_REGBOT_REGISTER_FAILURE_THRESHOLD
|
||||
JAMBONES_REGBOT_REGISTER_FAILURE_THRESHOLD,
|
||||
JAMBONES_SERVER_CONTROL
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const debug = require('debug')('jambonz:sbc-options-handler');
|
||||
const { isDrained } = require('./cli/feature-server-config');
|
||||
const serverControl = require('./server-control');
|
||||
const {
|
||||
EXPIRES_INTERVAL,
|
||||
CHECK_EXPIRES_INTERVAL,
|
||||
@@ -111,6 +112,27 @@ module.exports = ({srf, logger}) => {
|
||||
|
||||
return async(req, res) => {
|
||||
|
||||
/* server-control: topology discovery request (X-Jambonz-Discover: true).
|
||||
Deliberately answerable from any (external) IP - it is gated only by the
|
||||
JAMBONES_SERVER_CONTROL env var and the presence of the discovery header. */
|
||||
if (serverControl.isEnabled() && serverControl.isDiscoverRequest(req)) {
|
||||
try {
|
||||
const topology = await serverControl.discoverServers(req.srf);
|
||||
logger.info({source_address: req.source_address, topology},
|
||||
'responding to X-Jambonz-Discover OPTIONS request');
|
||||
res.send(200, {
|
||||
body: JSON.stringify(topology),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error({err}, 'Error handling discovery OPTIONS');
|
||||
res.send(503);
|
||||
}
|
||||
return req.srf.endSession(req);
|
||||
}
|
||||
|
||||
/* OPTIONS ping from internal FS or RTP server? */
|
||||
const internal = req.has('X-FS-Status') || req.has('X-RTP-Status');
|
||||
if (!internal) {
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
const debug = require('debug')('jambonz:sbc-server-control');
|
||||
const { JAMBONES_SERVER_CONTROL, JAMBONES_CLUSTER_ID } = require('../config');
|
||||
|
||||
/* SIP header a client sets (X-Jambonz-Discover: true) to request a topology discovery response */
|
||||
const DISCOVER_HEADER = 'X-Jambonz-Discover';
|
||||
|
||||
/**
|
||||
* Whether the server-control features are enabled.
|
||||
* Gated behind the JAMBONES_SERVER_CONTROL env var; treats the usual truthy
|
||||
* strings ('1', 'true', 'yes') as enabled, everything else as disabled.
|
||||
*/
|
||||
const isEnabled = () => {
|
||||
if (!JAMBONES_SERVER_CONTROL) return false;
|
||||
return /^(1|true|yes)$/i.test(`${JAMBONES_SERVER_CONTROL}`.trim());
|
||||
};
|
||||
|
||||
/* redis set names holding the active servers of each type for this cluster */
|
||||
const _setNames = () => {
|
||||
const prefix = JAMBONES_CLUSTER_ID || 'default';
|
||||
return {
|
||||
featureServers: `${prefix}:active-fs`,
|
||||
sipServers: `${prefix}:active-sip`,
|
||||
rtpServers: `${prefix}:active-rtp`
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if the request is a discovery request, i.e. it carries the
|
||||
* `X-Jambonz-Discover: true` header. Header presence/value is checked
|
||||
* case-insensitively.
|
||||
*/
|
||||
const isDiscoverRequest = (req) => {
|
||||
if (!req.has(DISCOVER_HEADER)) return false;
|
||||
return /^true$/i.test(`${req.get(DISCOVER_HEADER)}`.trim());
|
||||
};
|
||||
|
||||
/**
|
||||
* Read the current cluster topology from redis: the IPs of the feature
|
||||
* servers, SIP servers and RTP servers.
|
||||
* @param {object} srf - drachtio srf instance (uses srf.locals.retrieveSet)
|
||||
* @returns {Promise<{featureServers: string[], sipServers: string[], rtpServers: string[]}>}
|
||||
*/
|
||||
const discoverServers = async(srf) => {
|
||||
const { retrieveSet } = srf.locals;
|
||||
const { featureServers, sipServers, rtpServers } = _setNames();
|
||||
|
||||
const [fs, sip, rtp] = await Promise.all([
|
||||
retrieveSet(featureServers),
|
||||
retrieveSet(sipServers),
|
||||
retrieveSet(rtpServers)
|
||||
]);
|
||||
|
||||
const topology = {
|
||||
featureServers: fs || [],
|
||||
sipServers: sip || [],
|
||||
rtpServers: rtp || []
|
||||
};
|
||||
debug({topology}, 'discovered cluster topology from redis');
|
||||
return topology;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
DISCOVER_HEADER,
|
||||
isEnabled,
|
||||
isDiscoverRequest,
|
||||
discoverServers
|
||||
};
|
||||
Reference in New Issue
Block a user