jssip
Version:
the Javascript SIP library
366 lines (293 loc) • 12.6 kB
JavaScript
;
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; }
var Logger = require('./Logger');
var Utils = require('./Utils');
var JsSIP_C = require('./Constants');
var SIPMessage = require('./SIPMessage');
var RequestSender = require('./RequestSender');
var logger = new Logger('Registrator');
var MIN_REGISTER_EXPIRES = 10; // In seconds.
module.exports = /*#__PURE__*/function () {
function Registrator(ua, transport) {
_classCallCheck(this, Registrator);
// Force reg_id to 1.
this._reg_id = 1;
this._ua = ua;
this._transport = transport;
this._registrar = ua.configuration.registrar_server;
this._expires = ua.configuration.register_expires; // Call-ID and CSeq values RFC3261 10.2.
this._call_id = Utils.createRandomToken(22);
this._cseq = 0;
this._to_uri = ua.configuration.uri;
this._registrationTimer = null; // Ongoing Register request.
this._registering = false; // Set status.
this._registered = false; // Contact header.
this._contact = this._ua.contact.toString(); // Sip.ice media feature tag (RFC 5768).
this._contact += ';+sip.ice'; // Custom headers for REGISTER and un-REGISTER.
this._extraHeaders = []; // Custom Contact header params for REGISTER and un-REGISTER.
this._extraContactParams = ''; // Contents of the sip.instance Contact header parameter.
this._sipInstance = "\"<urn:uuid:".concat(this._ua.configuration.instance_id, ">\"");
this._contact += ";reg-id=".concat(this._reg_id);
this._contact += ";+sip.instance=".concat(this._sipInstance);
}
_createClass(Registrator, [{
key: "setExtraHeaders",
value: function setExtraHeaders(extraHeaders) {
if (!Array.isArray(extraHeaders)) {
extraHeaders = [];
}
this._extraHeaders = extraHeaders.slice();
}
}, {
key: "setExtraContactParams",
value: function setExtraContactParams(extraContactParams) {
if (!(extraContactParams instanceof Object)) {
extraContactParams = {};
} // Reset it.
this._extraContactParams = '';
for (var param_key in extraContactParams) {
if (Object.prototype.hasOwnProperty.call(extraContactParams, param_key)) {
var param_value = extraContactParams[param_key];
this._extraContactParams += ";".concat(param_key);
if (param_value) {
this._extraContactParams += "=".concat(param_value);
}
}
}
}
}, {
key: "register",
value: function register() {
var _this = this;
if (this._registering) {
logger.debug('Register request in progress...');
return;
}
var extraHeaders = this._extraHeaders.slice();
extraHeaders.push("Contact: ".concat(this._contact, ";expires=").concat(this._expires).concat(this._extraContactParams));
extraHeaders.push("Expires: ".concat(this._expires));
var request = new SIPMessage.OutgoingRequest(JsSIP_C.REGISTER, this._registrar, this._ua, {
'to_uri': this._to_uri,
'call_id': this._call_id,
'cseq': this._cseq += 1
}, extraHeaders);
var request_sender = new RequestSender(this._ua, request, {
onRequestTimeout: function onRequestTimeout() {
_this._registrationFailure(null, JsSIP_C.causes.REQUEST_TIMEOUT);
},
onTransportError: function onTransportError() {
_this._registrationFailure(null, JsSIP_C.causes.CONNECTION_ERROR);
},
// Increase the CSeq on authentication.
onAuthenticated: function onAuthenticated() {
_this._cseq += 1;
},
onReceiveResponse: function onReceiveResponse(response) {
// Discard responses to older REGISTER/un-REGISTER requests.
if (response.cseq !== _this._cseq) {
return;
} // Clear registration timer.
if (_this._registrationTimer !== null) {
clearTimeout(_this._registrationTimer);
_this._registrationTimer = null;
}
switch (true) {
case /^1[0-9]{2}$/.test(response.status_code):
{
// Ignore provisional responses.
break;
}
case /^2[0-9]{2}$/.test(response.status_code):
{
_this._registering = false;
if (!response.hasHeader('Contact')) {
logger.debug('no Contact header in response to REGISTER, response ignored');
break;
}
var contacts = response.headers['Contact'].reduce(function (a, b) {
return a.concat(b.parsed);
}, []); // Get the Contact pointing to us and update the expires value accordingly.
// Try to find a matching Contact using sip.instance and reg-id.
var contact = contacts.find(function (element) {
return _this._sipInstance === element.getParam('+sip.instance') && _this._reg_id === parseInt(element.getParam('reg-id'));
}); // If no match was found using the sip.instance try comparing the URIs.
if (!contact) {
contact = contacts.find(function (element) {
return element.uri.user === _this._ua.contact.uri.user;
});
}
if (!contact) {
logger.debug('no Contact header pointing to us, response ignored');
break;
}
var expires = contact.getParam('expires');
if (!expires && response.hasHeader('expires')) {
expires = response.getHeader('expires');
}
if (!expires) {
expires = _this._expires;
}
expires = Number(expires);
if (expires < MIN_REGISTER_EXPIRES) expires = MIN_REGISTER_EXPIRES;
var timeout = expires > 64 ? expires * 1000 / 2 + Math.floor((expires / 2 - 32) * 1000 * Math.random()) : expires * 1000 - 5000; // Re-Register or emit an event before the expiration interval has elapsed.
// For that, decrease the expires value. ie: 3 seconds.
_this._registrationTimer = setTimeout(function () {
_this._registrationTimer = null; // If there are no listeners for registrationExpiring, renew registration.
// If there are listeners, let the function listening do the register call.
if (_this._ua.listeners('registrationExpiring').length === 0) {
_this.register();
} else {
_this._ua.emit('registrationExpiring');
}
}, timeout); // Save gruu values.
if (contact.hasParam('temp-gruu')) {
_this._ua.contact.temp_gruu = contact.getParam('temp-gruu').replace(/"/g, '');
}
if (contact.hasParam('pub-gruu')) {
_this._ua.contact.pub_gruu = contact.getParam('pub-gruu').replace(/"/g, '');
}
if (!_this._registered) {
_this._registered = true;
_this._ua.registered({
response: response
});
}
break;
}
// Interval too brief RFC3261 10.2.8.
case /^423$/.test(response.status_code):
{
if (response.hasHeader('min-expires')) {
// Increase our registration interval to the suggested minimum.
_this._expires = Number(response.getHeader('min-expires'));
if (_this._expires < MIN_REGISTER_EXPIRES) _this._expires = MIN_REGISTER_EXPIRES; // Attempt the registration again immediately.
_this.register();
} else {
// This response MUST contain a Min-Expires header field.
logger.debug('423 response received for REGISTER without Min-Expires');
_this._registrationFailure(response, JsSIP_C.causes.SIP_FAILURE_CODE);
}
break;
}
default:
{
var cause = Utils.sipErrorCause(response.status_code);
_this._registrationFailure(response, cause);
}
}
}
});
this._registering = true;
request_sender.send();
}
}, {
key: "unregister",
value: function unregister() {
var _this2 = this;
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
if (!this._registered) {
logger.debug('already unregistered');
return;
}
this._registered = false; // Clear the registration timer.
if (this._registrationTimer !== null) {
clearTimeout(this._registrationTimer);
this._registrationTimer = null;
}
var extraHeaders = this._extraHeaders.slice();
if (options.all) {
extraHeaders.push("Contact: *".concat(this._extraContactParams));
} else {
extraHeaders.push("Contact: ".concat(this._contact, ";expires=0").concat(this._extraContactParams));
}
extraHeaders.push('Expires: 0');
var request = new SIPMessage.OutgoingRequest(JsSIP_C.REGISTER, this._registrar, this._ua, {
'to_uri': this._to_uri,
'call_id': this._call_id,
'cseq': this._cseq += 1
}, extraHeaders);
var request_sender = new RequestSender(this._ua, request, {
onRequestTimeout: function onRequestTimeout() {
_this2._unregistered(null, JsSIP_C.causes.REQUEST_TIMEOUT);
},
onTransportError: function onTransportError() {
_this2._unregistered(null, JsSIP_C.causes.CONNECTION_ERROR);
},
// Increase the CSeq on authentication.
onAuthenticated: function onAuthenticated() {
_this2._cseq += 1;
},
onReceiveResponse: function onReceiveResponse(response) {
switch (true) {
case /^1[0-9]{2}$/.test(response.status_code):
// Ignore provisional responses.
break;
case /^2[0-9]{2}$/.test(response.status_code):
_this2._unregistered(response);
break;
default:
{
var cause = Utils.sipErrorCause(response.status_code);
_this2._unregistered(response, cause);
}
}
}
});
request_sender.send();
}
}, {
key: "close",
value: function close() {
if (this._registered) {
this.unregister();
}
}
}, {
key: "onTransportClosed",
value: function onTransportClosed() {
this._registering = false;
if (this._registrationTimer !== null) {
clearTimeout(this._registrationTimer);
this._registrationTimer = null;
}
if (this._registered) {
this._registered = false;
this._ua.unregistered({});
}
}
}, {
key: "_registrationFailure",
value: function _registrationFailure(response, cause) {
this._registering = false;
this._ua.registrationFailed({
response: response || null,
cause: cause
});
if (this._registered) {
this._registered = false;
this._ua.unregistered({
response: response || null,
cause: cause
});
}
}
}, {
key: "_unregistered",
value: function _unregistered(response, cause) {
this._registering = false;
this._registered = false;
this._ua.unregistered({
response: response || null,
cause: cause || null
});
}
}, {
key: "registered",
get: function get() {
return this._registered;
}
}]);
return Registrator;
}();