jssip
Version:
the Javascript SIP library
175 lines (132 loc) • 6.2 kB
JavaScript
"use strict";
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 JsSIP_C = require('./Constants');
var DigestAuthentication = require('./DigestAuthentication');
var Transactions = require('./Transactions');
var logger = new Logger('RequestSender'); // Default event handlers.
var EventHandlers = {
onRequestTimeout: function onRequestTimeout() {},
onTransportError: function onTransportError() {},
onReceiveResponse: function onReceiveResponse() {},
onAuthenticated: function onAuthenticated() {}
};
module.exports = /*#__PURE__*/function () {
function RequestSender(ua, request, eventHandlers) {
_classCallCheck(this, RequestSender);
this._ua = ua;
this._eventHandlers = eventHandlers;
this._method = request.method;
this._request = request;
this._auth = null;
this._challenged = false;
this._staled = false; // Define the undefined handlers.
for (var handler in EventHandlers) {
if (Object.prototype.hasOwnProperty.call(EventHandlers, handler)) {
if (!this._eventHandlers[handler]) {
this._eventHandlers[handler] = EventHandlers[handler];
}
}
} // If ua is in closing process or even closed just allow sending Bye and ACK.
if (ua.status === ua.C.STATUS_USER_CLOSED && (this._method !== JsSIP_C.BYE || this._method !== JsSIP_C.ACK)) {
this._eventHandlers.onTransportError();
}
}
/**
* Create the client transaction and send the message.
*/
_createClass(RequestSender, [{
key: "send",
value: function send() {
var _this = this;
var eventHandlers = {
onRequestTimeout: function onRequestTimeout() {
_this._eventHandlers.onRequestTimeout();
},
onTransportError: function onTransportError() {
_this._eventHandlers.onTransportError();
},
onReceiveResponse: function onReceiveResponse(response) {
_this._receiveResponse(response);
}
};
switch (this._method) {
case 'INVITE':
this.clientTransaction = new Transactions.InviteClientTransaction(this._ua, this._ua.transport, this._request, eventHandlers);
break;
case 'ACK':
this.clientTransaction = new Transactions.AckClientTransaction(this._ua, this._ua.transport, this._request, eventHandlers);
break;
default:
this.clientTransaction = new Transactions.NonInviteClientTransaction(this._ua, this._ua.transport, this._request, eventHandlers);
} // If authorization JWT is present, use it.
if (this._ua._configuration.authorization_jwt) {
this._request.setHeader('Authorization', this._ua._configuration.authorization_jwt);
}
this.clientTransaction.send();
}
/**
* Called from client transaction when receiving a correct response to the request.
* Authenticate request if needed or pass the response back to the applicant.
*/
}, {
key: "_receiveResponse",
value: function _receiveResponse(response) {
var challenge;
var authorization_header_name;
var status_code = response.status_code;
/*
* Authentication
* Authenticate once. _challenged_ flag used to avoid infinite authentications.
*/
if ((status_code === 401 || status_code === 407) && (this._ua.configuration.password !== null || this._ua.configuration.ha1 !== null)) {
// Get and parse the appropriate WWW-Authenticate or Proxy-Authenticate header.
if (response.status_code === 401) {
challenge = response.parseHeader('www-authenticate');
authorization_header_name = 'authorization';
} else {
challenge = response.parseHeader('proxy-authenticate');
authorization_header_name = 'proxy-authorization';
} // Verify it seems a valid challenge.
if (!challenge) {
logger.debug("".concat(response.status_code, " with wrong or missing challenge, cannot authenticate"));
this._eventHandlers.onReceiveResponse(response);
return;
}
if (!this._challenged || !this._staled && challenge.stale === true) {
if (!this._auth) {
this._auth = new DigestAuthentication({
username: this._ua.configuration.authorization_user,
password: this._ua.configuration.password,
realm: this._ua.configuration.realm,
ha1: this._ua.configuration.ha1
});
} // Verify that the challenge is really valid.
if (!this._auth.authenticate(this._request, challenge)) {
this._eventHandlers.onReceiveResponse(response);
return;
}
this._challenged = true; // Update ha1 and realm in the UA.
this._ua.set('realm', this._auth.get('realm'));
this._ua.set('ha1', this._auth.get('ha1'));
if (challenge.stale) {
this._staled = true;
}
this._request = this._request.clone();
this._request.cseq += 1;
this._request.setHeader('cseq', "".concat(this._request.cseq, " ").concat(this._method));
this._request.setHeader(authorization_header_name, this._auth.toString());
this._eventHandlers.onAuthenticated(this._request);
this.send();
} else {
this._eventHandlers.onReceiveResponse(response);
}
} else {
this._eventHandlers.onReceiveResponse(response);
}
}
}]);
return RequestSender;
}();