First commit merge
This commit is contained in:
248
routes/indialog.js
Normal file
248
routes/indialog.js
Normal file
@@ -0,0 +1,248 @@
|
||||
const logger = require('../utils/logger');
|
||||
|
||||
/**
|
||||
* In-Dialog Handler - Equivalent to Kamailio's WITHINDLG route
|
||||
* Handles SIP requests within established dialogs (BYE, re-INVITE, etc.)
|
||||
*/
|
||||
class InDialogHandler {
|
||||
constructor(srf) {
|
||||
this.srf = srf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle media teardown for BYE/CANCEL (equivalent to kamailio.cfg:494-497)
|
||||
*/
|
||||
handleMediaTeardown(req) {
|
||||
if (req.method === 'BYE' || req.method === 'CANCEL') {
|
||||
logger.info('[WITHINDLG] Media teardown for %s | Call-ID: %s', req.method, req.get('Call-ID'));
|
||||
// RTP proxy cleanup would go here
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle media setup for ACK (equivalent to kamailio.cfg:500-502)
|
||||
*/
|
||||
handleMediaSetup(req) {
|
||||
if (req.method === 'ACK') {
|
||||
logger.info('[WITHINDLG] Media setup for ACK | Call-ID: %s', req.get('Call-ID'));
|
||||
// RTP proxy setup would go here
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process loose route headers (equivalent to kamailio.cfg:506-561)
|
||||
*/
|
||||
processLooseRoute(req, res) {
|
||||
const routeHeaders = req.get('Route');
|
||||
if (!routeHeaders) {
|
||||
return false;
|
||||
}
|
||||
|
||||
logger.info('[WITHINDLG] Route headers processed | Call-ID: %s', req.get('Call-ID'));
|
||||
|
||||
// Process Route headers to determine next hop
|
||||
const routes = Array.isArray(routeHeaders) ? routeHeaders : [routeHeaders];
|
||||
const nextRoute = routes[0];
|
||||
|
||||
if (nextRoute.includes(this.srf.localAddress)) {
|
||||
// Route points back to us - check if user is registered locally
|
||||
const aor = `${req.uri.user}@${req.uri.host}`;
|
||||
const registration = this.srf.registrationHandler?.lookup(aor);
|
||||
|
||||
if (registration) {
|
||||
logger.info('[WITHINDLG] Loose route resolved to local user, forwarding | Call-ID: %s', req.get('Call-ID'));
|
||||
return this.forwardToUser(req, res, registration);
|
||||
} else {
|
||||
logger.warn('[WITHINDLG] Loose route points to us but user not found locally | Call-ID: %s', req.get('Call-ID'));
|
||||
res.send(404, 'Not here');
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// Route is to a different destination, process normally
|
||||
logger.info('[WITHINDLG] Forwarding to different destination | Call-ID: %s', req.get('Call-ID'));
|
||||
return this.forwardToDestination(req, res, nextRoute);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward request to registered user
|
||||
*/
|
||||
async forwardToUser(req, res, registration) {
|
||||
try {
|
||||
const dlg = await this.srf.createRequest(req.method, registration.contact, {
|
||||
headers: {
|
||||
'From': req.get('From'),
|
||||
'To': req.get('To'),
|
||||
'Call-ID': req.get('Call-ID'),
|
||||
'CSeq': req.get('CSeq'),
|
||||
'Contact': req.get('Contact'),
|
||||
'Content-Type': req.get('Content-Type'),
|
||||
'Via': req.get('Via'),
|
||||
'Route': req.get('Route'),
|
||||
'Max-Forwards': req.get('Max-Forwards')
|
||||
},
|
||||
body: req.body
|
||||
});
|
||||
|
||||
logger.info('[WITHINDLG] Successfully forwarded to user | Call-ID: %s', req.get('Call-ID'));
|
||||
|
||||
// Send response back to originator
|
||||
res.send(200, 'OK');
|
||||
|
||||
// Relay responses
|
||||
this.relayResponses(dlg, req, res);
|
||||
|
||||
} catch (error) {
|
||||
logger.error('[WITHINDLG] Failed to forward to user: %s | Call-ID: %s', error.message, req.get('Call-ID'));
|
||||
res.send(500, 'Server Error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward request to destination
|
||||
*/
|
||||
async forwardToDestination(req, res, destination) {
|
||||
try {
|
||||
const dlg = await this.srf.createRequest(req.method, destination, {
|
||||
headers: {
|
||||
'From': req.get('From'),
|
||||
'To': req.get('To'),
|
||||
'Call-ID': req.get('Call-ID'),
|
||||
'CSeq': req.get('CSeq'),
|
||||
'Contact': req.get('Contact'),
|
||||
'Content-Type': req.get('Content-Type'),
|
||||
'Via': req.get('Via'),
|
||||
'Route': req.get('Route'),
|
||||
'Max-Forwards': req.get('Max-Forwards')
|
||||
},
|
||||
body: req.body
|
||||
});
|
||||
|
||||
logger.info('[WITHINDLG] Successfully forwarded to destination | Call-ID: %s', req.get('Call-ID'));
|
||||
|
||||
// Send response back to originator
|
||||
res.send(200, 'OK');
|
||||
|
||||
// Relay responses
|
||||
this.relayResponses(dlg, req, res);
|
||||
|
||||
} catch (error) {
|
||||
logger.error('[WITHINDLG] Failed to forward to destination: %s | Call-ID: %s', error.message, req.get('Call-ID'));
|
||||
res.send(500, 'Server Error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Relay responses between dialogs
|
||||
*/
|
||||
relayResponses(uacDialog, uasReq, uasRes) {
|
||||
// Relay UAC responses back to UAS
|
||||
uacDialog.on('response', (response) => {
|
||||
uasRes.send(response.status, response.reason, response.headers);
|
||||
});
|
||||
|
||||
// Handle UAS responses to UAC
|
||||
uasRes.on('response', (response) => {
|
||||
uacDialog.send(response.status, response.reason, response.headers);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle BYE request (equivalent to kamailio.cfg:508-511)
|
||||
*/
|
||||
handleBye(req, res) {
|
||||
logger.info('[WITHINDLG] BYE request | Call-ID: %s', req.get('Call-ID'));
|
||||
|
||||
// Handle media teardown
|
||||
this.handleMediaTeardown(req);
|
||||
|
||||
// Process routing
|
||||
if (this.processLooseRoute(req, res)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If no loose route, try to find the target user
|
||||
const aor = `${req.uri.user}@${req.uri.host}`;
|
||||
const registration = this.srf.registrationHandler?.lookup(aor);
|
||||
|
||||
if (registration) {
|
||||
logger.info('[WITHINDLG] BYE: Target user found locally, forwarding | Call-ID: %s', req.get('Call-ID'));
|
||||
this.forwardToUser(req, res, registration);
|
||||
} else {
|
||||
logger.warn('[WITHINDLG] BYE: Target user not found locally | Call-ID: %s', req.get('Call-ID'));
|
||||
res.send(404, 'Not here');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle ACK request (equivalent to kamailio.cfg:611-621)
|
||||
*/
|
||||
handleAck(req, res) {
|
||||
logger.info('[WITHINDLG] ACK request | Call-ID: %s', req.get('Call-ID'));
|
||||
|
||||
// Handle media setup
|
||||
this.handleMediaSetup(req);
|
||||
|
||||
// ACK is typically just absorbed
|
||||
res.send(200, 'OK');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle in-dialog SUBSCRIBE (equivalent to kamailio.cfg:606-610)
|
||||
*/
|
||||
handleSubscribe(req, res) {
|
||||
logger.info('[WITHINDLG] SUBSCRIBE request | Call-ID: %s', req.get('Call-ID'));
|
||||
res.send(404, 'Not here');
|
||||
}
|
||||
|
||||
/**
|
||||
* Main in-dialog request handler (equivalent to kamailio.cfg:489-627)
|
||||
*/
|
||||
async handleInDialog(req, res) {
|
||||
const method = req.method;
|
||||
const callId = req.get('Call-ID');
|
||||
|
||||
logger.info('[WITHINDLG] %s request | Call-ID: %s', method, callId);
|
||||
|
||||
// Handle media-related operations
|
||||
if (method === 'BYE' || method === 'CANCEL') {
|
||||
this.handleMediaTeardown(req);
|
||||
} else if (method === 'ACK') {
|
||||
this.handleMediaSetup(req);
|
||||
}
|
||||
|
||||
// Process based on method
|
||||
switch (method) {
|
||||
case 'BYE':
|
||||
this.handleBye(req, res);
|
||||
break;
|
||||
|
||||
case 'ACK':
|
||||
this.handleAck(req, res);
|
||||
break;
|
||||
|
||||
case 'SUBSCRIBE':
|
||||
this.handleSubscribe(req, res);
|
||||
break;
|
||||
|
||||
default:
|
||||
// For other methods, try to process Route headers
|
||||
if (!this.processLooseRoute(req, res)) {
|
||||
// No route found, try direct forwarding
|
||||
logger.info('[WITHINDLG] No route headers, forwarding to destination | Call-ID: %s', callId);
|
||||
const aor = `${req.uri.user}@${req.uri.host}`;
|
||||
const registration = this.srf.registrationHandler?.lookup(aor);
|
||||
|
||||
if (registration) {
|
||||
this.forwardToUser(req, res, registration);
|
||||
} else {
|
||||
logger.warn('[WITHINDLG] No route found for %s | Call-ID: %s', method, callId);
|
||||
res.send(404, 'Not here');
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = InDialogHandler;
|
||||
Reference in New Issue
Block a user