libsignal-net: propagate close code from manual close

Co-authored-by: Alex Bakon <akonradi@signal.org>
This commit is contained in:
automated-signal 2025-01-16 16:46:45 -06:00 committed by GitHub
parent 13feacf5c2
commit aa6bf51189
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -473,7 +473,11 @@ export class LibsignalWebSocketResource
extends EventTarget extends EventTarget
implements IWebSocketResource implements IWebSocketResource
{ {
closed = false; // The reason that the connection was closed, if it was closed.
//
// When setting this to anything other than `undefined`, the "close" event
// must be dispatched.
#closedReasonCode?: number;
// Unlike WebSocketResource, libsignal will automatically attempt to keep the // Unlike WebSocketResource, libsignal will automatically attempt to keep the
// socket alive using websocket pings, so we don't need a timer-based // socket alive using websocket pings, so we don't need a timer-based
@ -511,19 +515,16 @@ export class LibsignalWebSocketResource
} }
public close(code = NORMAL_DISCONNECT_CODE, reason?: string): void { public close(code = NORMAL_DISCONNECT_CODE, reason?: string): void {
if (this.closed) { if (this.#closedReasonCode !== undefined) {
log.info(`${this.logId}.close: Already closed! ${code}/${reason}`); log.info(`${this.logId}.close: Already closed! ${code}/${reason}`);
return; return;
} }
this.#closedReasonCode = code;
drop(this.chatService.disconnect()); drop(this.chatService.disconnect());
// On linux the socket can wait a long time to emit its close event if we've // Since we set `closedReasonCode`, we must dispatch the close event.
// lost the internet connection. On the order of minutes. This speeds that this.dispatchEvent(new CloseEvent(code, reason || 'no reason provided'));
// process up.
Timers.setTimeout(
() => this.onConnectionInterrupted(null),
5 * durations.SECOND
);
} }
public shutdown(): void { public shutdown(): void {
@ -531,22 +532,24 @@ export class LibsignalWebSocketResource
} }
onConnectionInterrupted(cause: LibSignalError | null): void { onConnectionInterrupted(cause: LibSignalError | null): void {
if (this.closed) { if (this.#closedReasonCode !== undefined) {
log.warn( if (cause != null) {
`${this.logId}.onConnectionInterrupted called after resource is closed` // This can happen normally if there's a race between a disconnect
// request and an error on the connection. It's likely benign but in
// case it's not, make sure we know about it.
log.info(
`${this.logId}: onConnectionInterrupted called after resource is closed: ${cause.message}`
); );
}
return; return;
} }
this.closed = true;
log.warn(`${this.logId}: connection closed`); log.warn(`${this.logId}: connection closed`);
let event; const event = cause
if (cause) { ? new CloseEvent(UNEXPECTED_DISCONNECT_CODE, cause.message)
event = new CloseEvent(UNEXPECTED_DISCONNECT_CODE, cause.message); : // The cause was an intentional disconnect. Report normal closure.
} else { new CloseEvent(NORMAL_DISCONNECT_CODE, 'normal');
// The cause was an intentional disconnect. Report normal closure. this.#closedReasonCode = event.code;
event = new CloseEvent(NORMAL_DISCONNECT_CODE, 'normal');
}
this.dispatchEvent(event); this.dispatchEvent(event);
} }
@ -1141,7 +1144,7 @@ class KeepAliveSender {
} catch (error) { } catch (error) {
this.wsr.close( this.wsr.close(
UNEXPECTED_DISCONNECT_CODE, UNEXPECTED_DISCONNECT_CODE,
'No response to keepalive request' `No response to keepalive request after ${timeout}ms`
); );
return false; return false;
} }