mirror of
https://github.com/xtr-dev/rondevu-client.git
synced 2025-12-10 02:43:25 +00:00
Fix critical ICE candidate timing bug
ICE candidate handler was being set up AFTER setLocalDescription, but ICE gathering starts when setLocalDescription is called. This meant candidates were generated before the handler was attached, so they were never sent to the server, causing connection failures. Fixed by: - Setting up ICE handler BEFORE setLocalDescription in both offer and answer flows - Changed setupIceCandidateHandler() to use this.peer.offerId instead of parameter - Handler now checks this.peer.offerId before sending (waits for it to be set) Order of operations now: 1. Set up ICE candidate handler 2. Call setLocalDescription (ICE gathering starts) 3. Set this.peer.offerId (handler can now send candidates) This ensures all ICE candidates are captured and sent to the server. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -23,16 +23,16 @@ export class AnsweringState extends PeerState {
|
|||||||
sdp: offerSdp
|
sdp: offerSdp
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Enable trickle ICE - set up handler before ICE gathering starts
|
||||||
|
this.setupIceCandidateHandler();
|
||||||
|
|
||||||
// Create answer
|
// Create answer
|
||||||
const answer = await this.peer.pc.createAnswer();
|
const answer = await this.peer.pc.createAnswer();
|
||||||
await this.peer.pc.setLocalDescription(answer);
|
await this.peer.pc.setLocalDescription(answer); // ICE gathering starts here
|
||||||
|
|
||||||
// Send answer to server immediately (don't wait for ICE)
|
// Send answer to server immediately (don't wait for ICE)
|
||||||
await this.peer.offersApi.answer(offerId, answer.sdp!);
|
await this.peer.offersApi.answer(offerId, answer.sdp!);
|
||||||
|
|
||||||
// Enable trickle ICE - send candidates as they arrive
|
|
||||||
this.setupIceCandidateHandler(offerId);
|
|
||||||
|
|
||||||
// Transition to exchanging ICE
|
// Transition to exchanging ICE
|
||||||
const { ExchangingIceState } = await import('./exchanging-ice-state.js');
|
const { ExchangingIceState } = await import('./exchanging-ice-state.js');
|
||||||
this.peer.setState(new ExchangingIceState(this.peer, offerId, options));
|
this.peer.setState(new ExchangingIceState(this.peer, offerId, options));
|
||||||
|
|||||||
@@ -24,9 +24,13 @@ export class CreatingOfferState extends PeerState {
|
|||||||
this.peer.emitEvent('datachannel', channel);
|
this.peer.emitEvent('datachannel', channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enable trickle ICE - set up handler before ICE gathering starts
|
||||||
|
// Handler will check this.peer.offerId before sending
|
||||||
|
this.setupIceCandidateHandler();
|
||||||
|
|
||||||
// Create WebRTC offer
|
// Create WebRTC offer
|
||||||
const offer = await this.peer.pc.createOffer();
|
const offer = await this.peer.pc.createOffer();
|
||||||
await this.peer.pc.setLocalDescription(offer);
|
await this.peer.pc.setLocalDescription(offer); // ICE gathering starts here
|
||||||
|
|
||||||
// Send offer to server immediately (don't wait for ICE)
|
// Send offer to server immediately (don't wait for ICE)
|
||||||
const offers = await this.peer.offersApi.create([{
|
const offers = await this.peer.offersApi.create([{
|
||||||
@@ -36,10 +40,7 @@ export class CreatingOfferState extends PeerState {
|
|||||||
}]);
|
}]);
|
||||||
|
|
||||||
const offerId = offers[0].id;
|
const offerId = offers[0].id;
|
||||||
this.peer.offerId = offerId;
|
this.peer.offerId = offerId; // Now handler can send candidates
|
||||||
|
|
||||||
// Enable trickle ICE - send candidates as they arrive
|
|
||||||
this.setupIceCandidateHandler(offerId);
|
|
||||||
|
|
||||||
// Transition to waiting for answer
|
// Transition to waiting for answer
|
||||||
const { WaitingForAnswerState } = await import('./waiting-for-answer-state.js');
|
const { WaitingForAnswerState } = await import('./waiting-for-answer-state.js');
|
||||||
|
|||||||
@@ -35,13 +35,13 @@ export abstract class PeerState {
|
|||||||
* Setup trickle ICE candidate handler
|
* Setup trickle ICE candidate handler
|
||||||
* Sends local ICE candidates to server as they are discovered
|
* Sends local ICE candidates to server as they are discovered
|
||||||
*/
|
*/
|
||||||
protected setupIceCandidateHandler(offerId: string): void {
|
protected setupIceCandidateHandler(): void {
|
||||||
this.iceCandidateHandler = async (event: RTCPeerConnectionIceEvent) => {
|
this.iceCandidateHandler = async (event: RTCPeerConnectionIceEvent) => {
|
||||||
if (event.candidate && offerId) {
|
if (event.candidate && this.peer.offerId) {
|
||||||
const candidateData = event.candidate.toJSON();
|
const candidateData = event.candidate.toJSON();
|
||||||
if (candidateData.candidate && candidateData.candidate !== '') {
|
if (candidateData.candidate && candidateData.candidate !== '') {
|
||||||
try {
|
try {
|
||||||
await this.peer.offersApi.addIceCandidates(offerId, [candidateData]);
|
await this.peer.offersApi.addIceCandidates(this.peer.offerId, [candidateData]);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error sending ICE candidate:', err);
|
console.error('Error sending ICE candidate:', err);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user