UNPKG

jssip

Version:

the Javascript SIP library

825 lines (692 loc) 25.3 kB
"use strict"; function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } var EventEmitter = require('events').EventEmitter; var Logger = require('./Logger'); var JsSIP_C = require('./Constants'); var SIPMessage = require('./SIPMessage'); var Timers = require('./Timers'); var loggernict = new Logger('NonInviteClientTransaction'); var loggerict = new Logger('InviteClientTransaction'); var loggeract = new Logger('AckClientTransaction'); var loggernist = new Logger('NonInviteServerTransaction'); var loggerist = new Logger('InviteServerTransaction'); var C = { // Transaction states. STATUS_TRYING: 1, STATUS_PROCEEDING: 2, STATUS_CALLING: 3, STATUS_ACCEPTED: 4, STATUS_COMPLETED: 5, STATUS_TERMINATED: 6, STATUS_CONFIRMED: 7, // Transaction types. NON_INVITE_CLIENT: 'nict', NON_INVITE_SERVER: 'nist', INVITE_CLIENT: 'ict', INVITE_SERVER: 'ist' }; var NonInviteClientTransaction = /*#__PURE__*/function (_EventEmitter) { _inherits(NonInviteClientTransaction, _EventEmitter); var _super = _createSuper(NonInviteClientTransaction); function NonInviteClientTransaction(ua, transport, request, eventHandlers) { var _this; _classCallCheck(this, NonInviteClientTransaction); _this = _super.call(this); _this.type = C.NON_INVITE_CLIENT; _this.id = "z9hG4bK".concat(Math.floor(Math.random() * 10000000)); _this.ua = ua; _this.transport = transport; _this.request = request; _this.eventHandlers = eventHandlers; var via = "SIP/2.0/".concat(transport.via_transport); via += " ".concat(ua.configuration.via_host, ";branch=").concat(_this.id); _this.request.setHeader('via', via); _this.ua.newTransaction(_assertThisInitialized(_this)); return _this; } _createClass(NonInviteClientTransaction, [{ key: "stateChanged", value: function stateChanged(state) { this.state = state; this.emit('stateChanged'); } }, { key: "send", value: function send() { var _this2 = this; this.stateChanged(C.STATUS_TRYING); this.F = setTimeout(function () { _this2.timer_F(); }, Timers.TIMER_F); if (!this.transport.send(this.request)) { this.onTransportError(); } } }, { key: "onTransportError", value: function onTransportError() { loggernict.debug("transport error occurred, deleting transaction ".concat(this.id)); clearTimeout(this.F); clearTimeout(this.K); this.stateChanged(C.STATUS_TERMINATED); this.ua.destroyTransaction(this); this.eventHandlers.onTransportError(); } }, { key: "timer_F", value: function timer_F() { loggernict.debug("Timer F expired for transaction ".concat(this.id)); this.stateChanged(C.STATUS_TERMINATED); this.ua.destroyTransaction(this); this.eventHandlers.onRequestTimeout(); } }, { key: "timer_K", value: function timer_K() { this.stateChanged(C.STATUS_TERMINATED); this.ua.destroyTransaction(this); } }, { key: "receiveResponse", value: function receiveResponse(response) { var _this3 = this; var status_code = response.status_code; if (status_code < 200) { switch (this.state) { case C.STATUS_TRYING: case C.STATUS_PROCEEDING: this.stateChanged(C.STATUS_PROCEEDING); this.eventHandlers.onReceiveResponse(response); break; } } else { switch (this.state) { case C.STATUS_TRYING: case C.STATUS_PROCEEDING: this.stateChanged(C.STATUS_COMPLETED); clearTimeout(this.F); if (status_code === 408) { this.eventHandlers.onRequestTimeout(); } else { this.eventHandlers.onReceiveResponse(response); } this.K = setTimeout(function () { _this3.timer_K(); }, Timers.TIMER_K); break; case C.STATUS_COMPLETED: break; } } } }, { key: "C", get: function get() { return C; } }]); return NonInviteClientTransaction; }(EventEmitter); var InviteClientTransaction = /*#__PURE__*/function (_EventEmitter2) { _inherits(InviteClientTransaction, _EventEmitter2); var _super2 = _createSuper(InviteClientTransaction); function InviteClientTransaction(ua, transport, request, eventHandlers) { var _this4; _classCallCheck(this, InviteClientTransaction); _this4 = _super2.call(this); _this4.type = C.INVITE_CLIENT; _this4.id = "z9hG4bK".concat(Math.floor(Math.random() * 10000000)); _this4.ua = ua; _this4.transport = transport; _this4.request = request; _this4.eventHandlers = eventHandlers; request.transaction = _assertThisInitialized(_this4); var via = "SIP/2.0/".concat(transport.via_transport); via += " ".concat(ua.configuration.via_host, ";branch=").concat(_this4.id); _this4.request.setHeader('via', via); _this4.ua.newTransaction(_assertThisInitialized(_this4)); return _this4; } _createClass(InviteClientTransaction, [{ key: "stateChanged", value: function stateChanged(state) { this.state = state; this.emit('stateChanged'); } }, { key: "send", value: function send() { var _this5 = this; this.stateChanged(C.STATUS_CALLING); this.B = setTimeout(function () { _this5.timer_B(); }, Timers.TIMER_B); if (!this.transport.send(this.request)) { this.onTransportError(); } } }, { key: "onTransportError", value: function onTransportError() { clearTimeout(this.B); clearTimeout(this.D); clearTimeout(this.M); if (this.state !== C.STATUS_ACCEPTED) { loggerict.debug("transport error occurred, deleting transaction ".concat(this.id)); this.eventHandlers.onTransportError(); } this.stateChanged(C.STATUS_TERMINATED); this.ua.destroyTransaction(this); } // RFC 6026 7.2. }, { key: "timer_M", value: function timer_M() { loggerict.debug("Timer M expired for transaction ".concat(this.id)); if (this.state === C.STATUS_ACCEPTED) { clearTimeout(this.B); this.stateChanged(C.STATUS_TERMINATED); this.ua.destroyTransaction(this); } } // RFC 3261 17.1.1. }, { key: "timer_B", value: function timer_B() { loggerict.debug("Timer B expired for transaction ".concat(this.id)); if (this.state === C.STATUS_CALLING) { this.stateChanged(C.STATUS_TERMINATED); this.ua.destroyTransaction(this); this.eventHandlers.onRequestTimeout(); } } }, { key: "timer_D", value: function timer_D() { loggerict.debug("Timer D expired for transaction ".concat(this.id)); clearTimeout(this.B); this.stateChanged(C.STATUS_TERMINATED); this.ua.destroyTransaction(this); } }, { key: "sendACK", value: function sendACK(response) { var _this6 = this; var ack = new SIPMessage.OutgoingRequest(JsSIP_C.ACK, this.request.ruri, this.ua, { 'route_set': this.request.getHeaders('route'), 'call_id': this.request.getHeader('call-id'), 'cseq': this.request.cseq }); ack.setHeader('from', this.request.getHeader('from')); ack.setHeader('via', this.request.getHeader('via')); ack.setHeader('to', response.getHeader('to')); this.D = setTimeout(function () { _this6.timer_D(); }, Timers.TIMER_D); this.transport.send(ack); } }, { key: "cancel", value: function cancel(reason) { // Send only if a provisional response (>100) has been received. if (this.state !== C.STATUS_PROCEEDING) { return; } var cancel = new SIPMessage.OutgoingRequest(JsSIP_C.CANCEL, this.request.ruri, this.ua, { 'route_set': this.request.getHeaders('route'), 'call_id': this.request.getHeader('call-id'), 'cseq': this.request.cseq }); cancel.setHeader('from', this.request.getHeader('from')); cancel.setHeader('via', this.request.getHeader('via')); cancel.setHeader('to', this.request.getHeader('to')); if (reason) { cancel.setHeader('reason', reason); } this.transport.send(cancel); } }, { key: "receiveResponse", value: function receiveResponse(response) { var _this7 = this; var status_code = response.status_code; if (status_code >= 100 && status_code <= 199) { switch (this.state) { case C.STATUS_CALLING: this.stateChanged(C.STATUS_PROCEEDING); this.eventHandlers.onReceiveResponse(response); break; case C.STATUS_PROCEEDING: this.eventHandlers.onReceiveResponse(response); break; } } else if (status_code >= 200 && status_code <= 299) { switch (this.state) { case C.STATUS_CALLING: case C.STATUS_PROCEEDING: this.stateChanged(C.STATUS_ACCEPTED); this.M = setTimeout(function () { _this7.timer_M(); }, Timers.TIMER_M); this.eventHandlers.onReceiveResponse(response); break; case C.STATUS_ACCEPTED: this.eventHandlers.onReceiveResponse(response); break; } } else if (status_code >= 300 && status_code <= 699) { switch (this.state) { case C.STATUS_CALLING: case C.STATUS_PROCEEDING: this.stateChanged(C.STATUS_COMPLETED); this.sendACK(response); this.eventHandlers.onReceiveResponse(response); break; case C.STATUS_COMPLETED: this.sendACK(response); break; } } } }, { key: "C", get: function get() { return C; } }]); return InviteClientTransaction; }(EventEmitter); var AckClientTransaction = /*#__PURE__*/function (_EventEmitter3) { _inherits(AckClientTransaction, _EventEmitter3); var _super3 = _createSuper(AckClientTransaction); function AckClientTransaction(ua, transport, request, eventHandlers) { var _this8; _classCallCheck(this, AckClientTransaction); _this8 = _super3.call(this); _this8.id = "z9hG4bK".concat(Math.floor(Math.random() * 10000000)); _this8.transport = transport; _this8.request = request; _this8.eventHandlers = eventHandlers; var via = "SIP/2.0/".concat(transport.via_transport); via += " ".concat(ua.configuration.via_host, ";branch=").concat(_this8.id); _this8.request.setHeader('via', via); return _this8; } _createClass(AckClientTransaction, [{ key: "send", value: function send() { if (!this.transport.send(this.request)) { this.onTransportError(); } } }, { key: "onTransportError", value: function onTransportError() { loggeract.debug("transport error occurred for transaction ".concat(this.id)); this.eventHandlers.onTransportError(); } }, { key: "C", get: function get() { return C; } }]); return AckClientTransaction; }(EventEmitter); var NonInviteServerTransaction = /*#__PURE__*/function (_EventEmitter4) { _inherits(NonInviteServerTransaction, _EventEmitter4); var _super4 = _createSuper(NonInviteServerTransaction); function NonInviteServerTransaction(ua, transport, request) { var _this9; _classCallCheck(this, NonInviteServerTransaction); _this9 = _super4.call(this); _this9.type = C.NON_INVITE_SERVER; _this9.id = request.via_branch; _this9.ua = ua; _this9.transport = transport; _this9.request = request; _this9.last_response = ''; request.server_transaction = _assertThisInitialized(_this9); _this9.state = C.STATUS_TRYING; ua.newTransaction(_assertThisInitialized(_this9)); return _this9; } _createClass(NonInviteServerTransaction, [{ key: "stateChanged", value: function stateChanged(state) { this.state = state; this.emit('stateChanged'); } }, { key: "timer_J", value: function timer_J() { loggernist.debug("Timer J expired for transaction ".concat(this.id)); this.stateChanged(C.STATUS_TERMINATED); this.ua.destroyTransaction(this); } }, { key: "onTransportError", value: function onTransportError() { if (!this.transportError) { this.transportError = true; loggernist.debug("transport error occurred, deleting transaction ".concat(this.id)); clearTimeout(this.J); this.stateChanged(C.STATUS_TERMINATED); this.ua.destroyTransaction(this); } } }, { key: "receiveResponse", value: function receiveResponse(status_code, response, onSuccess, onFailure) { var _this10 = this; if (status_code === 100) { /* RFC 4320 4.1 * 'A SIP element MUST NOT * send any provisional response with a * Status-Code other than 100 to a non-INVITE request.' */ switch (this.state) { case C.STATUS_TRYING: this.stateChanged(C.STATUS_PROCEEDING); if (!this.transport.send(response)) { this.onTransportError(); } break; case C.STATUS_PROCEEDING: this.last_response = response; if (!this.transport.send(response)) { this.onTransportError(); if (onFailure) { onFailure(); } } else if (onSuccess) { onSuccess(); } break; } } else if (status_code >= 200 && status_code <= 699) { switch (this.state) { case C.STATUS_TRYING: case C.STATUS_PROCEEDING: this.stateChanged(C.STATUS_COMPLETED); this.last_response = response; this.J = setTimeout(function () { _this10.timer_J(); }, Timers.TIMER_J); if (!this.transport.send(response)) { this.onTransportError(); if (onFailure) { onFailure(); } } else if (onSuccess) { onSuccess(); } break; case C.STATUS_COMPLETED: break; } } } }, { key: "C", get: function get() { return C; } }]); return NonInviteServerTransaction; }(EventEmitter); var InviteServerTransaction = /*#__PURE__*/function (_EventEmitter5) { _inherits(InviteServerTransaction, _EventEmitter5); var _super5 = _createSuper(InviteServerTransaction); function InviteServerTransaction(ua, transport, request) { var _this11; _classCallCheck(this, InviteServerTransaction); _this11 = _super5.call(this); _this11.type = C.INVITE_SERVER; _this11.id = request.via_branch; _this11.ua = ua; _this11.transport = transport; _this11.request = request; _this11.last_response = ''; request.server_transaction = _assertThisInitialized(_this11); _this11.state = C.STATUS_PROCEEDING; ua.newTransaction(_assertThisInitialized(_this11)); _this11.resendProvisionalTimer = null; request.reply(100); return _this11; } _createClass(InviteServerTransaction, [{ key: "stateChanged", value: function stateChanged(state) { this.state = state; this.emit('stateChanged'); } }, { key: "timer_H", value: function timer_H() { loggerist.debug("Timer H expired for transaction ".concat(this.id)); if (this.state === C.STATUS_COMPLETED) { loggerist.debug('ACK not received, dialog will be terminated'); } this.stateChanged(C.STATUS_TERMINATED); this.ua.destroyTransaction(this); } }, { key: "timer_I", value: function timer_I() { this.stateChanged(C.STATUS_TERMINATED); this.ua.destroyTransaction(this); } // RFC 6026 7.1. }, { key: "timer_L", value: function timer_L() { loggerist.debug("Timer L expired for transaction ".concat(this.id)); if (this.state === C.STATUS_ACCEPTED) { this.stateChanged(C.STATUS_TERMINATED); this.ua.destroyTransaction(this); } } }, { key: "onTransportError", value: function onTransportError() { if (!this.transportError) { this.transportError = true; loggerist.debug("transport error occurred, deleting transaction ".concat(this.id)); if (this.resendProvisionalTimer !== null) { clearInterval(this.resendProvisionalTimer); this.resendProvisionalTimer = null; } clearTimeout(this.L); clearTimeout(this.H); clearTimeout(this.I); this.stateChanged(C.STATUS_TERMINATED); this.ua.destroyTransaction(this); } } }, { key: "resend_provisional", value: function resend_provisional() { if (!this.transport.send(this.last_response)) { this.onTransportError(); } } // INVITE Server Transaction RFC 3261 17.2.1. }, { key: "receiveResponse", value: function receiveResponse(status_code, response, onSuccess, onFailure) { var _this12 = this; if (status_code >= 100 && status_code <= 199) { switch (this.state) { case C.STATUS_PROCEEDING: if (!this.transport.send(response)) { this.onTransportError(); } this.last_response = response; break; } } if (status_code > 100 && status_code <= 199 && this.state === C.STATUS_PROCEEDING) { // Trigger the resendProvisionalTimer only for the first non 100 provisional response. if (this.resendProvisionalTimer === null) { this.resendProvisionalTimer = setInterval(function () { _this12.resend_provisional(); }, Timers.PROVISIONAL_RESPONSE_INTERVAL); } } else if (status_code >= 200 && status_code <= 299) { switch (this.state) { case C.STATUS_PROCEEDING: this.stateChanged(C.STATUS_ACCEPTED); this.last_response = response; this.L = setTimeout(function () { _this12.timer_L(); }, Timers.TIMER_L); if (this.resendProvisionalTimer !== null) { clearInterval(this.resendProvisionalTimer); this.resendProvisionalTimer = null; } /* falls through */ case C.STATUS_ACCEPTED: // Note that this point will be reached for proceeding this.state also. if (!this.transport.send(response)) { this.onTransportError(); if (onFailure) { onFailure(); } } else if (onSuccess) { onSuccess(); } break; } } else if (status_code >= 300 && status_code <= 699) { switch (this.state) { case C.STATUS_PROCEEDING: if (this.resendProvisionalTimer !== null) { clearInterval(this.resendProvisionalTimer); this.resendProvisionalTimer = null; } if (!this.transport.send(response)) { this.onTransportError(); if (onFailure) { onFailure(); } } else { this.stateChanged(C.STATUS_COMPLETED); this.H = setTimeout(function () { _this12.timer_H(); }, Timers.TIMER_H); if (onSuccess) { onSuccess(); } } break; } } } }, { key: "C", get: function get() { return C; } }]); return InviteServerTransaction; }(EventEmitter); /** * INVITE: * _true_ if retransmission * _false_ new request * * ACK: * _true_ ACK to non2xx response * _false_ ACK must be passed to TU (accepted state) * ACK to 2xx response * * CANCEL: * _true_ no matching invite transaction * _false_ matching invite transaction and no final response sent * * OTHER: * _true_ retransmission * _false_ new request */ function checkTransaction(_ref, request) { var _transactions = _ref._transactions; var tr; switch (request.method) { case JsSIP_C.INVITE: tr = _transactions.ist[request.via_branch]; if (tr) { switch (tr.state) { case C.STATUS_PROCEEDING: tr.transport.send(tr.last_response); break; // RFC 6026 7.1 Invite retransmission. // Received while in C.STATUS_ACCEPTED state. Absorb it. case C.STATUS_ACCEPTED: break; } return true; } break; case JsSIP_C.ACK: tr = _transactions.ist[request.via_branch]; // RFC 6026 7.1. if (tr) { if (tr.state === C.STATUS_ACCEPTED) { return false; } else if (tr.state === C.STATUS_COMPLETED) { tr.state = C.STATUS_CONFIRMED; tr.I = setTimeout(function () { tr.timer_I(); }, Timers.TIMER_I); return true; } } // ACK to 2XX Response. else { return false; } break; case JsSIP_C.CANCEL: tr = _transactions.ist[request.via_branch]; if (tr) { request.reply_sl(200); if (tr.state === C.STATUS_PROCEEDING) { return false; } else { return true; } } else { request.reply_sl(481); return true; } default: // Non-INVITE Server Transaction RFC 3261 17.2.2. tr = _transactions.nist[request.via_branch]; if (tr) { switch (tr.state) { case C.STATUS_TRYING: break; case C.STATUS_PROCEEDING: case C.STATUS_COMPLETED: tr.transport.send(tr.last_response); break; } return true; } break; } } module.exports = { C: C, NonInviteClientTransaction: NonInviteClientTransaction, InviteClientTransaction: InviteClientTransaction, AckClientTransaction: AckClientTransaction, NonInviteServerTransaction: NonInviteServerTransaction, InviteServerTransaction: InviteServerTransaction, checkTransaction: checkTransaction };