Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 67 additions & 42 deletions src/Session.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Session = function (sessionDescriptionHandlerFactory) {
throw new SIP.Exceptions.SessionDescriptionHandlerMissing('A session description handler is required for the session to function');
}
this.sessionDescriptionHandlerFactory = sessionDescriptionHandlerFactory;
// this.sessionDescriptionHandler gets set by ICC/ISC constructors

this.hasOffer = false;
this.hasAnswer = false;

Expand Down Expand Up @@ -362,28 +362,50 @@ Session.prototype = {
* @private
*/
receiveReinvite: function(request) {
var self = this;
var self = this,
promise;
// TODO: Should probably check state of the session

self.emit('reinvite', this);

// Invite w/o SDP
if (request.getHeader('Content-Length') === '0' && !request.getHeader('Content-Type')) {
promise = this.sessionDescriptionHandler.getDescription(this.sessionDescriptionHandlerOptions, this.modifiers);

if (!this.sessionDescriptionHandler.hasDescription(request.getHeader('Content-Type'))) {
this.logger.warn('invalid Content-Type');
// Invite w/ SDP
} else if (this.sessionDescriptionHandler.hasDescription(request.getHeader('Content-Type'))) {
promise = this.sessionDescriptionHandler.setDescription(request.body, this.sessionDescriptionHandlerOptions, this.modifiers)
.then(this.sessionDescriptionHandler.getDescription.bind(this.sessionDescriptionHandler, this.sessionDescriptionHandlerOptions, this.modifiers));

// Bad Packet (should never get hit)
} else {
request.reply(415);
this.emit('reinviteFailed', self);
return;
}

self.emit('reinvite', this);
var _receiveRequest = this.receiveRequest;

this.sessionDescriptionHandler.setDescription(request.body, this.sessionDescriptionHandlerOptions, this.modifiers)
.then(this.sessionDescriptionHandler.getDescription.bind(this.sessionDescriptionHandler, this.sessionDescriptionHandlerOptions, this.modifiers))
.then(function(description) {
var extraHeaders = ['Contact: ' + self.contact];
request.reply(200, null, extraHeaders, description,
function() {
self.status = C.STATUS_WAITING_FOR_ACK;
self.setACKTimer();
self.emit('reinviteAccepted', self);
});
})
.catch(function onFailure (e) {
// HACK to catch the ACK
this.receiveRequest = function(request) {
if (request.method === SIP.C.ACK &&
this.status === C.STATUS_WAITING_FOR_ACK &&
this.sessionDescriptionHandler.hasDescription(request.getHeader('Content-Type'))) {
this.hasAnswer = true;
this.sessionDescriptionHandler.setDescription(request.body, this.sessionDescriptionHandlerOptions, this.modifiers)
.then(function() {
SIP.Timers.clearTimeout(this.timers.ackTimer);
SIP.Timers.clearTimeout(this.timers.invite2xxTimer);
this.status = C.STATUS_CONFIRMED;

this.emit('confirmed', request);
}.bind(this));
} else {
_receiveRequest.call(this, request);
}
}.bind(this);

promise.catch(function onFailure (e) {
var statusCode;
if (e instanceof SIP.Exceptions.GetDescriptionError) {
statusCode = 500;
Expand All @@ -397,6 +419,16 @@ Session.prototype = {
}
request.reply(statusCode);
self.emit('reinviteFailed', self);
})
.then(function(description) {
var extraHeaders = ['Contact: ' + self.contact];
request.reply(200, null, extraHeaders, description,
function() {
self.status = C.STATUS_WAITING_FOR_ACK;

self.setACKTimer();
self.emit('reinviteAccepted', self);
});
});
},

Expand Down Expand Up @@ -528,7 +560,6 @@ Session.prototype = {
case /^2[0-9]{2}$/.test(response.status_code):
this.status = C.STATUS_CONFIRMED;

// TODO: Handle re-INVITE w/o SDP
// 17.1.1.1 - For each final response that is received at the client transaction, the client transaction sends an ACK,
this.emit("ack", response.transaction.sendACK());
this.pendingReinvite = false;
Expand Down Expand Up @@ -1142,30 +1173,23 @@ InviteServerContext.prototype = {
break;
case SIP.C.ACK:
if(this.status === C.STATUS_WAITING_FOR_ACK) {
if (!this.hasAnswer) {
this.sessionDescriptionHandler = this.setupSessionDescriptionHandler();
if(this.sessionDescriptionHandler.hasDescription(request.getHeader('Content-Type'))) {
// ACK contains answer to an INVITE w/o SDP negotiation
this.hasAnswer = true;
this.sessionDescriptionHandler.setDescription(request.body, this.sessionDescriptionHandlerOptions, this.modifiers)
.then(
confirmSession.bind(this),
function onFailure (e) {
this.logger.warn(e);
this.terminate({
statusCode: '488',
reasonPhrase: 'Bad Media Description'
});
this.failed(request, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
this.terminated(request, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
}.bind(this)
);
} else if (this.early_sdp) {
confirmSession.apply(this);
} else {
this.failed(request, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
this.terminated(request, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
}
if(this.sessionDescriptionHandler.hasDescription(request.getHeader('Content-Type'))) {
// ACK contains answer to an INVITE w/o SDP negotiation
this.hasAnswer = true;
this.sessionDescriptionHandler.setDescription(request.body, this.sessionDescriptionHandlerOptions, this.modifiers)
.then(
// TODO: Catch then .then
confirmSession.bind(this),
function onFailure (e) {
this.logger.warn(e);
this.terminate({
statusCode: '488',
reasonPhrase: 'Bad Media Description'
});
this.failed(request, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
this.terminated(request, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
}.bind(this)
);
} else {
confirmSession.apply(this);
}
Expand Down Expand Up @@ -1740,6 +1764,7 @@ InviteClientContext.prototype = {
}

if (request.method === SIP.C.ACK && this.status === C.STATUS_WAITING_FOR_ACK) {

SIP.Timers.clearTimeout(this.timers.ackTimer);
SIP.Timers.clearTimeout(this.timers.invite2xxTimer);
this.status = C.STATUS_CONFIRMED;
Expand Down
1 change: 1 addition & 0 deletions test/helpers/SIPMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ window.SIPHelper = {
response.data = null;

response.body = (body || 'foo').toString();
response.setHeader('Content-Type', 'rps');

/*
* We aren't going to parse a bunch of strings,
Expand Down
5 changes: 4 additions & 1 deletion test/helpers/rps.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,10 @@ RPSMediaHandler.prototype = {
},

hasDescription: function (message) {
return true;
if (message) {
return true;
}
return false;
},

setDescription: function (sessionDescription, constraints, modifiers) {
Expand Down
110 changes: 0 additions & 110 deletions test/spec/InviteClientContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,116 +199,6 @@ describe('An INVITE sent from a UAC', function () {
});
});


/**
OPTIONS
-------

Documented:

media
RTCConstraints -> rtcConstraints
extraHeaders
anonymous

Undocumented:

inviteWithoutSdp -> offer
renderbody -> renderBody
rendertype -> renderType
params

Unimplemented:

rel100/100rel (Currently in UA configuration)
*/
/* FIXME - This is out of scope for this suite.
* It should test that the options get passed through,
* as well as any sugar performed by the session,
* but otherwise these should be in the realm of
* MediaHandler tests.
*/
xdescribe('with options.media', function () {
var gumSpy;

it('not defined, defaults to audio+video', function (done) {
// Don't need the old session.
this.session.terminate();

gumSpy = spyOn(SIP.WebRTC, 'getUserMedia').and.callFake(function() {
expect(gumSpy.calls.mostRecent().args[0]).toEqual({
audio: true,
video: true
});
return SIP.Utils.Promise.resolve().then(function () {
setTimeout(done, 0);
});
});
this.session = this.ua.invite('alice@example.com', this.session_options);
});

it('defined as constraints, follows those constraints', function (done) {
var myConstraints;

this.session.terminate();

gumSpy = spyOn(SIP.WebRTC, 'getUserMedia').and.callFake(function() {
expect(gumSpy.calls.mostRecent().args[0]).toEqual(myConstraints);
return SIP.Utils.Promise.resolve().then(function () {
setTimeout(done, 0);
});
});

myConstraints = {
audio: ['Anything', 'Goes', 'Here'],
video: {
resolution: 'da best',
frame_rate: 'lowsy'
},
telepathy: {
basic: 100
}
};
this.session_options.media = {constraints: myConstraints};

this.session = this.ua.invite('alice@example.com', this.session_options);
});

xit('TODO defined as stream, uses the stream', function () {
// TODO
});

xit('TODO defined as manager, uses the manager', function () {
// TODO
});
});
describe('with options.mediaStream', function () {
});
describe('with options.rtcConstraints', function () {

});
describe('with options.extraHeaders', function () {

});
describe('with options.params', function () {

});
describe('with options.anonymous', function () {

});
describe('with options.offer', function () {

});
describe('with options.renderBody', function () {

});
describe('with options.renderType', function () {

});
describe('with options.rel100', function () {

});

/**
*
* 1xx Response texts (Progress)
Expand Down
42 changes: 24 additions & 18 deletions test/spec/SpecSession.js
Original file line number Diff line number Diff line change
Expand Up @@ -336,16 +336,6 @@ describe('Session', function() {
};
});

it('does not call setDescription and replies with 415 if contentType is not application/sdp', function() {
spyOn(message, 'getHeader').and.returnValue('incorrect');
spyOn(message, 'reply');

Session.receiveReinvite(message);

expect(Session.sessionDescriptionHandler.setDescription).not.toHaveBeenCalled();
expect(message.reply).toHaveBeenCalledWith(415);
});

it('calls setDescription on success', function() {
Session.receiveReinvite(message);

Expand Down Expand Up @@ -1129,6 +1119,16 @@ describe('InviteServerContext', function() {
it('calls sessionDescriptionHandler.setDescription when the ACK contains an answer to an invite w/o sdp', function() {
InviteServerContext.hasOffer = true;

InviteServerContext.sessionDescriptionHandler = {
hasDescription: function() {
return true;
},
setDescription: jasmine.createSpy('setDescription').and.returnValue(SIP.Utils.Promise.resolve(true)),
close: function() {
return;
}
};

req = SIP.Parser.parseMessage([
'ACK sip:gled5gsn@hk95bautgaa7.invalid;transport=ws;aor=james%40onsnip.onsip.com SIP/2.0',
'Max-Forwards: 65',
Expand Down Expand Up @@ -1158,6 +1158,13 @@ describe('InviteServerContext', function() {

InviteServerContext.dialog = new SIP.Dialog(InviteServerContext, req, 'UAS');

InviteServerContext.sessionDescriptionHandler = {
hasDescription: function() {
return false;
},
close: function() { return; }
};

InviteServerContext.receiveRequest(req);

expect(SIP.Timers.clearTimeout).toHaveBeenCalledWith(InviteServerContext.timers.ackTimer);
Expand All @@ -1167,14 +1174,6 @@ describe('InviteServerContext', function() {
expect(InviteServerContext.accepted).not.toHaveBeenCalled();
});

it('calls failed if the above two conditions are not true', function() {
spyOn(InviteServerContext, 'failed');

InviteServerContext.receiveRequest(req);

expect(InviteServerContext.failed).toHaveBeenCalledWith(req, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
});

it('calls confirmSession if there was an invite w/ sdp originally', function() {
InviteServerContext.hasOffer = true;
InviteServerContext.hasAnswer = true;
Expand All @@ -1183,6 +1182,13 @@ describe('InviteServerContext', function() {
spyOn(InviteServerContext, 'emit');
InviteServerContext.dialog = new SIP.Dialog(InviteServerContext, req, 'UAS');

InviteServerContext.sessionDescriptionHandler = {
hasDescription: function() {
return false;
},
close: function() { return; }
};

InviteServerContext.receiveRequest(req);

expect(SIP.Timers.clearTimeout).toHaveBeenCalledWith(InviteServerContext.timers.ackTimer);
Expand Down