Skip to content
Open
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
9 changes: 8 additions & 1 deletion src/api/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -923,7 +923,14 @@ export abstract class Session {
protected onInviteRequest(request: IncomingInviteRequest): void {
this.logger.log("Session.onInviteRequest");
if (this.state !== SessionState.Established) {
this.logger.error(`INVITE received while in state ${this.state}, dropping request`);
// Refuse it rather than drop it: rejecting completes the server
// transaction, without which no re-INVITE of ours can be sent for the
// rest of the dialog. 491 invites the sender to retry once we settle.
this.logger.warn(`INVITE received while in state ${this.state}, rejecting with 491`);
const outgoingResponse = request.reject({ statusCode: 491 });
if (this.delegate && this.delegate.onInvite) {
this.delegate.onInvite(request.message, outgoingResponse.message, 491);
}
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,37 @@ export class SessionDescriptionHandler implements SessionDescriptionHandlerDefin
}
}

/**
* Discards an offer the peer connection has applied but not completed.
*
* @remarks
* Called when an offer/answer exchange fails, so that the next one starts
* from a stable peer connection. Without it a failed exchange leaves the
* offer in place and every attempt after it fails while creating its
* description, on a session that is otherwise still up.
*
* A stable connection has nothing to discard, which is not an error.
*/
public rollbackDescription(): Promise<void> {
this.logger.debug("SessionDescriptionHandler.rollbackDescription");
if (this._peerConnection === undefined) {
return Promise.reject(new Error("Peer connection closed."));
}
switch (this._peerConnection.signalingState) {
case "stable":
return Promise.resolve();
case "have-local-offer":
return this._peerConnection.setLocalDescription({ type: "rollback" });
case "have-remote-offer":
return this._peerConnection.setRemoteDescription({ type: "rollback" });
case "have-local-pranswer":
case "have-remote-pranswer":
case "closed":
default:
return Promise.reject(new Error("Invalid signaling state " + this._peerConnection.signalingState));
}
}

/**
* Send DTMF via RTP (RFC 4733).
* Returns true if DTMF send is successful, false otherwise.
Expand Down
35 changes: 35 additions & 0 deletions test/spec/platform/web/session-description-handler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,41 @@ describe("Web SessionDescriptionHandler", () => {
});
});

describe("sdh1 rollbackDescription", () => {
beforeEach(() => {
resetSpies();
});

it("leaves a stable peer connection stable", async () => {
await sdh1.rollbackDescription();
expect(sdh1.peerConnection?.signalingState).toBe("stable");
});

it("discards an offer it created", async () => {
await sdh1.getDescription();
expect(sdh1.peerConnection?.signalingState).toBe("have-local-offer");
await sdh1.rollbackDescription();
expect(sdh1.peerConnection?.signalingState).toBe("stable");
});

it("discards an offer it received", async () => {
const offer = await sdh2.getDescription();
await sdh1.setDescription(offer.body);
expect(sdh1.peerConnection?.signalingState).toBe("have-remote-offer");
await sdh1.rollbackDescription();
expect(sdh1.peerConnection?.signalingState).toBe("stable");
});

it("lets a description be created again", async () => {
await sdh1.getDescription();
await sdh1.rollbackDescription();
// Without the rollback this rejects, which is what leaves a session
// unable to renegotiate after one failed attempt.
const offer = await sdh1.getDescription();
expect(offer.contentType).toBe("application/sdp");
});
});

describe("sdh1 getDescription", () => {
let offer: BodyAndContentType | undefined;
let answer: BodyAndContentType | undefined;
Expand Down