diff --git a/lib/session/call-session.js b/lib/session/call-session.js index 653462da..8fe8aba2 100644 --- a/lib/session/call-session.js +++ b/lib/session/call-session.js @@ -780,9 +780,19 @@ class CallSession extends Emitter { async _onReinvite(req, res) { try { - const newSdp = await this.ep.modify(req.body); - res.send(200, {body: newSdp}); - this.logger.info({offer: req.body, answer: newSdp}, 'handling reINVITE'); + if (this.ep) { + const newSdp = await this.ep.modify(req.body); + res.send(200, {body: newSdp}); + this.logger.info({offer: req.body, answer: newSdp}, 'handling reINVITE'); + } + else if (this.currentTask && this.currentTask.name === TaskName.Dial) { + this.logger.info('handling reINVITE after media has been released'); + await this.currentTask.handleReinviteAfterMediaReleased(req, res); + } + else { + this.logger.info('got reINVITE but no endpoint and media has not been released'); + res.send(488); + } } catch (err) { this.logger.error(err, 'Error handling reinvite'); } @@ -953,6 +963,13 @@ class CallSession extends Emitter { }); } + async handleReinviteAfterMediaReleased(req, res) { + assert(this.dlg && this.dlg.connected && !this.ep); + const sdp = await this.dlg.modify(req.body); + this.logger.info({sdp}, 'CallSession:handleReinviteAfterMediaReleased - reinvite to A leg returned sdp'); + res.send(200, {body: sdp}); + } + /** * Called any time call status changes. This method both invokes the * call_status_hook callback as well as updates the realtime database diff --git a/lib/tasks/dial.js b/lib/tasks/dial.js index 3d1cf323..c533f050 100644 --- a/lib/tasks/dial.js +++ b/lib/tasks/dial.js @@ -13,6 +13,7 @@ const placeCall = require('../utils/place-outdial'); const sessionTracker = require('../session/session-tracker'); const DtmfCollector = require('../utils/dtmf-collector'); const dbUtils = require('../utils/db-utils'); +const { rmSync } = require('fs'); const debug = require('debug')('jambonz:feature-server'); function parseDtmfOptions(logger, dtmfCapture) { @@ -437,6 +438,13 @@ class TaskDial extends Task { this.kill(cs); } }) + .on('reinvite', (req, res) => { + try { + cs.handleReinviteAfterMediaReleased(req, res); + } catch (err) { + this.logger.error(err, 'Error in dial einvite from B leg'); + } + }) .once('adulting', () => { /* child call just adulted and got its own session */ this.logger.info('Dial:on_adulting: detaching child call leg'); @@ -563,6 +571,12 @@ class TaskDial extends Task { await Promise.all([sd.reAnchorMedia(), cs.reAnchorMedia()]); this.epOther = cs.ep; } + + async handleReinviteAfterMediaReleased(req, res) { + const sdp = await this.dlg.modify(req.body); + this.logger.info({sdp}, 'Dial:handleReinviteAfterMediaReleased - sent reinvite to B leg'); + res.send(200, {body: sdp}); + } } module.exports = TaskDial; diff --git a/lib/utils/place-outdial.js b/lib/utils/place-outdial.js index 1182ad09..7fd1be70 100644 --- a/lib/utils/place-outdial.js +++ b/lib/utils/place-outdial.js @@ -197,9 +197,15 @@ class SingleDialer extends Emitter { .on('refresh', () => this.logger.info('SingleDialer:exec - dialog refreshed by uas')) .on('modify', async(req, res) => { try { - const newSdp = await this.ep.modify(req.body); - res.send(200, {body: newSdp}); - this.logger.info({offer: req.body, answer: newSdp}, 'SingleDialer:exec: handling reINVITE'); + if (this.ep) { + const newSdp = await this.ep.modify(req.body); + res.send(200, {body: newSdp}); + this.logger.info({offer: req.body, answer: newSdp}, 'SingleDialer:exec: handling reINVITE'); + } + else { + this.logger.info('SingleDialer:exec: handling reINVITE with released media, emit event'); + this.emit('reinvite', req, res); + } } catch (err) { this.logger.error(err, 'Error handling reinvite'); } diff --git a/test/docker-compose-testbed.yaml b/test/docker-compose-testbed.yaml index c05a9cb5..4e01d947 100644 --- a/test/docker-compose-testbed.yaml +++ b/test/docker-compose-testbed.yaml @@ -9,6 +9,7 @@ networks: services: mysql: image: mysql:5.7 + platform: linux/x86_64 ports: - "3360:3306" environment: