Skip to content

Commit

Permalink
Late SDP implementation
Browse files Browse the repository at this point in the history
Accept receiving SDP-less INVITE requests
  • Loading branch information
jmillan committed Oct 20, 2014
1 parent 390f4b5 commit 9a1ebdf
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 107 deletions.
1 change: 1 addition & 0 deletions src/Constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ JsSIP.C= {
NOT_FOUND: 'Not Found',
ADDRESS_INCOMPLETE: 'Address Incomplete',
INCOMPATIBLE_SDP: 'Incompatible SDP',
MISSING_SDP: 'Missing SDP',
AUTHENTICATION_ERROR: 'Authentication Error',
DIALOG_ERROR: 'Dialog Error',

Expand Down
278 changes: 171 additions & 107 deletions src/RTCSession.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ RTCSession = function(ua) {
this.earlyDialogs = {};
this.rtcMediaHandler = null;

// RTCSession confirmation flag
this.is_confirmed = false;

// is late SDP being negotiated
this.late_sdp = false;

// Session Timers
this.timers = {
ackTimer: null,
Expand Down Expand Up @@ -286,11 +292,19 @@ RTCSession.prototype.answer = function(options) {
return;
}

self.rtcMediaHandler.createAnswer(
answerCreationSucceeded,
answerCreationFailed,
RTCAnswerConstraints
);
if (self.late_sdp) {
self.rtcMediaHandler.createOffer(
sdpCreationSucceeded,
sdpCreationFailed,
RTCAnswerConstraints
);
} else {
self.rtcMediaHandler.createAnswer(
sdpCreationSucceeded,
sdpCreationFailed,
RTCAnswerConstraints
);
}
},

// rtcMediaHandler.addStream failed
Expand All @@ -302,8 +316,8 @@ RTCSession.prototype.answer = function(options) {
self.failed('system', null, JsSIP.C.causes.WEBRTC_ERROR);
},

// rtcMediaHandler.createAnswer succeeded
answerCreationSucceeded = function(body) {
// rtcMediaHandler.createAnswer or rtcMediaHandler.createOffer succeeded
sdpCreationSucceeded = function(body) {
var
// run for reply success callback
replySucceeded = function() {
Expand All @@ -326,8 +340,8 @@ RTCSession.prototype.answer = function(options) {
);
},

// rtcMediaHandler.createAnsewr failed
answerCreationFailed = function() {
// rtcMediaHandler.createAnswer or rtcMediaHandler.createOffer failed
sdpCreationFailed = function() {
if (self.status === C.STATUS_TERMINATED) {
return;
}
Expand Down Expand Up @@ -804,10 +818,45 @@ RTCSession.prototype.getRemoteStreams = function() {
RTCSession.prototype.init_incoming = function(request) {
var expires,
self = this,
contentType = request.getHeader('Content-Type');
contentType = request.getHeader('Content-Type'),

waitForAnswer = function() {
self.status = C.STATUS_WAITING_FOR_ANSWER;

// Set userNoAnswerTimer
self.timers.userNoAnswerTimer = window.setTimeout(function() {
request.reply(408);
self.failed('local',null, JsSIP.C.causes.NO_ANSWER);
}, self.ua.configuration.no_answer_timeout
);

/* Set expiresTimer
* RFC3261 13.3.1
*/
if (expires) {
self.timers.expiresTimer = window.setTimeout(function() {
if(self.status === C.STATUS_WAITING_FOR_ANSWER) {
request.reply(487);
self.failed('system', null, JsSIP.C.causes.EXPIRES);
}
}, expires
);
}

// Fire 'newRTCSession' event.
self.newRTCSession('remote', request);

// Reply 180.
request.reply(180, null, ['Contact: ' + self.contact]);

// Fire 'progress' event.
// TODO: Document that 'response' field in 'progress' event is null for
// incoming calls.
self.progress('local', null);
};

// Check body and content type
if(!request.body || (contentType !== 'application/sdp')) {
if(request.body && (contentType !== 'application/sdp')) {
request.reply(415);
return;
}
Expand Down Expand Up @@ -845,57 +894,29 @@ RTCSession.prototype.init_incoming = function(request) {
constraints: {"optional": [{'DtlsSrtpKeyAgreement': 'true'}]}
});

this.rtcMediaHandler.onMessage(
'offer',
request.body,
/*
* onSuccess
* SDP Offer is valid. Fire UA newRTCSession
*/
function() {
self.status = C.STATUS_WAITING_FOR_ANSWER;

// Set userNoAnswerTimer
self.timers.userNoAnswerTimer = window.setTimeout(function() {
request.reply(408);
self.failed('local',null, JsSIP.C.causes.NO_ANSWER);
}, self.ua.configuration.no_answer_timeout
);

/* Set expiresTimer
* RFC3261 13.3.1
if (request.body) {
this.rtcMediaHandler.onMessage(
'offer',
request.body,
/*
* onSuccess
* SDP Offer is valid. Fire UA newRTCSession
*/
if (expires) {
self.timers.expiresTimer = window.setTimeout(function() {
if(self.status === C.STATUS_WAITING_FOR_ANSWER) {
request.reply(487);
self.failed('system', null, JsSIP.C.causes.EXPIRES);
}
}, expires
);
waitForAnswer,
/*
* onFailure
* Bad media description
*/
function(e) {
self.logger.warn('invalid SDP');
self.logger.warn(e);
request.reply(488);
}

// Fire 'newRTCSession' event.
self.newRTCSession('remote', request);

// Reply 180.
request.reply(180, null, ['Contact: ' + self.contact]);

// Fire 'progress' event.
// TODO: Document that 'response' field in 'progress' event is null for
// incoming calls.
self.progress('local', null);
},
/*
* onFailure
* Bad media description
*/
function(e) {
self.logger.warn('invalid SDP');
self.logger.warn(e);
request.reply(488);
}
);
);
} else {
this.late_sdp = true;
waitForAnswer();
}
};

RTCSession.prototype.connect = function(target, options) {
Expand Down Expand Up @@ -1106,39 +1127,18 @@ RTCSession.prototype.receiveReinvite = function(request) {
sdp, idx, direction,
self = this,
contentType = request.getHeader('Content-Type'),
hold = true;

if (! request.body) {
this.logger.warn('re-INVITE without body not implemented');
request.reply(415, 're-INVITE Without Body Not Implemented');
return;
}
hold = false,

if (contentType !== 'application/sdp') {
this.logger.warn('invalid Content-Type');
request.reply(415);
return;
}

sdp = JsSIP.Parser.parseSDP(request.body);

for (idx=0; idx < sdp.media.length; idx++) {
direction = sdp.direction || sdp.media[idx].direction || 'sendrecv';

if (direction !== 'sendonly' && direction !== 'inactive') {
hold = false;
}
}
createSdp = function(onSuccess, onFailure) {
if (self.late_sdp) {
self.rtcMediaHandler.createOffer(onSuccess, onFailure);
} else {
self.rtcMediaHandler.createAnswer(onSuccess, onFailure);
}
},

this.rtcMediaHandler.onMessage(
'offer',
request.body,
/*
* onSuccess
* SDP Offer is valid
*/
function() {
self.rtcMediaHandler.createAnswer(
answer = function() {
createSdp(
// onSuccess
function(body) {
request.reply(200, null, ['Contact: ' + self.contact], body,
Expand All @@ -1160,16 +1160,47 @@ RTCSession.prototype.receiveReinvite = function(request) {
request.reply(500);
}
);
},
/*
* onFailure
* Bad media description
*/
function(e) {
self.logger.error(e);
request.reply(488);
};


if (request.body) {
if (contentType !== 'application/sdp') {
this.logger.warn('invalid Content-Type');
request.reply(415);
return;
}
);

sdp = JsSIP.Parser.parseSDP(request.body);

for (idx=0; idx < sdp.media.length; idx++) {
direction = sdp.direction || sdp.media[idx].direction || 'sendrecv';

if (direction === 'sendonly' || direction === 'inactive') {
hold = true;
}
}

this.rtcMediaHandler.onMessage(
'offer',
request.body,
/*
* onSuccess
* SDP Offer is valid
*/
answer,
/*
* onFailure
* Bad media description
*/
function(e) {
self.logger.error(e);
request.reply(488);
}
);
} else {
this.late_sdp = true;
answer();
}
};

/**
Expand Down Expand Up @@ -1244,7 +1275,8 @@ RTCSession.prototype.receiveUpdate = function(request) {
* In dialog Request Reception
*/
RTCSession.prototype.receiveRequest = function(request) {
var contentType;
var contentType,
self = this;

if(request.method === JsSIP.C.CANCEL) {
/* RFC3261 15 States that a UAS may have accepted an invitation while a CANCEL
Expand All @@ -1270,8 +1302,40 @@ RTCSession.prototype.receiveRequest = function(request) {
if(this.status === C.STATUS_WAITING_FOR_ACK) {
window.clearTimeout(this.timers.ackTimer);
window.clearTimeout(this.timers.invite2xxTimer);
this.status = C.STATUS_CONFIRMED;
this.confirmed('remote', request);

if (this.late_sdp) {
if (!request.body) {
self.ended('remote', request, JsSIP.C.causes.MISSING_SDP);
break;
}

this.rtcMediaHandler.onMessage(
'answer',
request.body,
/*
* onSuccess
* SDP Answer fits with Offer. Media will start
*/
function() {
self.late_sdp = false;
self.status = C.STATUS_CONFIRMED;
},
/*
* onFailure
* SDP Answer does not fit the Offer. Accept the call and Terminate.
*/
function(e) {
self.logger.warn(e);
self.ended('remote', request, JsSIP.C.causes.BAD_MEDIA_DESCRIPTION);
}
);
} else {
this.status = C.STATUS_CONFIRMED;
}

if (this.status === C.STATUS_CONFIRMED && !this.is_confirmed) {
this.confirmed('remote', request);
}
}
break;
case JsSIP.C.BYE:
Expand Down Expand Up @@ -1579,7 +1643,7 @@ RTCSession.prototype.receiveInviteResponse = function(response) {
this.status = C.STATUS_CONFIRMED;

if(!response.body) {
this.acceptAndTerminate(response, 400, 'Missing session description');
this.acceptAndTerminate(response, 400, JsSIP.C.causes.MISSING_SDP);
this.failed('remote', response, JsSIP.C.causes.BAD_MEDIA_DESCRIPTION);
break;
}
Expand All @@ -1597,10 +1661,8 @@ RTCSession.prototype.receiveInviteResponse = function(response) {
* SDP Answer fits with Offer. Media will start
*/
function() {
var ack;

session.accepted('remote', response);
ack = session.sendRequest(JsSIP.C.ACK);
session.sendRequest(JsSIP.C.ACK);
session.confirmed('local', null);
},
/*
Expand Down Expand Up @@ -1812,6 +1874,8 @@ RTCSession.prototype.confirmed = function(originator, ack) {
var session = this,
event_name = 'confirmed';

this.is_confirmed = true;

session.emit(event_name, session, {
originator: originator,
ack: ack || null
Expand Down

0 comments on commit 9a1ebdf

Please sign in to comment.