0
0
mirror of https://github.com/signalapp/libsignal.git synced 2024-09-19 11:32:17 +02:00

swift: Expose the reason a ChatService was disconnected

This commit is contained in:
Jordan Rose 2024-09-06 18:30:47 -07:00
parent a2702a1435
commit aaa481aa9a
5 changed files with 38 additions and 18 deletions

View File

@ -17,7 +17,7 @@ type ReceivedIncomingMessage = extern "C" fn(
cleanup: *mut ServerMessageAck,
);
type ReceivedQueueEmpty = extern "C" fn(ctx: *mut c_void);
type ConnectionInterrupted = extern "C" fn(ctx: *mut c_void);
type ConnectionInterrupted = extern "C" fn(ctx: *mut c_void, error: *mut SignalFfiError);
type DestroyChatListener = extern "C" fn(ctx: *mut c_void);
/// Callbacks for [`ChatListener`].
@ -77,8 +77,14 @@ impl ChatListener for ChatListenerStruct {
(self.0.received_queue_empty)(self.0.ctx)
}
// TODO: pass `_disconnect_cause` to `connection_interrupted`
fn connection_interrupted(&mut self, _disconnect_cause: ChatServiceError) {
(self.0.connection_interrupted)(self.0.ctx)
fn connection_interrupted(&mut self, disconnect_cause: ChatServiceError) {
let error = match disconnect_cause {
ChatServiceError::ServiceIntentionallyDisconnected => None,
c => Some(Box::new(SignalFfiError::from(c))),
};
(self.0.connection_interrupted)(
self.0.ctx,
error.map_or(std::ptr::null_mut(), Box::into_raw),
)
}
}

View File

@ -12,13 +12,7 @@ public protocol ConnectionEventsListener<Service>: AnyObject {
/// Called when the client gets disconnected from the server.
///
/// This includes both deliberate disconnects as well as unexpected socket closures.
///
/// The default implementation of this method does nothing.
func connectionWasInterrupted(_ service: Service)
}
extension ConnectionEventsListener {
func connectionWasInterrupted(_: Service) {}
func connectionWasInterrupted(_ service: Service, error: Error?)
}
public protocol ChatListener: ConnectionEventsListener<AuthenticatedChatService> {
@ -89,13 +83,15 @@ internal class ChatListenerBridge {
bridge.chatListener.chatServiceDidReceiveQueueEmpty(chatService)
}
let connectionInterrupted: SignalConnectionInterrupted = { rawCtx in
let connectionInterrupted: SignalConnectionInterrupted = { rawCtx, maybeError in
let bridge = Unmanaged<ChatListenerBridge>.fromOpaque(rawCtx!).takeUnretainedValue()
guard let chatService = bridge.chatService else {
return
}
bridge.chatListener.connectionWasInterrupted(chatService)
let error = convertError(maybeError)
bridge.chatListener.connectionWasInterrupted(chatService, error: error)
}
return .init(
@ -130,13 +126,15 @@ internal final class UnauthConnectionEventsListenerBridge {
let receivedQueueEmpty: SignalReceivedQueueEmpty = { _ in
fatalError("not used for the unauth listener")
}
let connectionInterrupted: SignalConnectionInterrupted = { rawCtx in
let connectionInterrupted: SignalConnectionInterrupted = { rawCtx, maybeError in
let bridge = Unmanaged<UnauthConnectionEventsListenerBridge>.fromOpaque(rawCtx!).takeUnretainedValue()
guard let chatService = bridge.chatService else {
return
}
bridge.listener.connectionWasInterrupted(chatService)
let error = convertError(maybeError)
bridge.listener.connectionWasInterrupted(chatService, error: error)
}
return .init(

View File

@ -70,6 +70,19 @@ public enum SignalError: Error {
internal typealias SignalFfiErrorRef = OpaquePointer
internal func convertError(_ error: SignalFfiErrorRef?) -> Error? {
// It would be *slightly* more efficient for checkError to call convertError,
// instead of the other way around. However, then it would be harder to implement
// checkError, since some of the conversion operations can themselves throw.
// So this is more maintainable.
do {
try checkError(error)
return nil
} catch let thrownError {
return thrownError
}
}
internal func checkError(_ error: SignalFfiErrorRef?) throws {
guard let error = error else { return }

View File

@ -634,7 +634,7 @@ typedef void (*SignalReceivedIncomingMessage)(void *ctx, SignalOwnedBuffer envel
typedef void (*SignalReceivedQueueEmpty)(void *ctx);
typedef void (*SignalConnectionInterrupted)(void *ctx);
typedef void (*SignalConnectionInterrupted)(void *ctx, SignalFfiError *error);
typedef void (*SignalDestroyChatListener)(void *ctx);

View File

@ -197,8 +197,9 @@ final class ChatServiceTests: TestCaseBase {
self.queueEmpty.fulfill()
}
func connectionWasInterrupted(_: AuthenticatedChatService) {
func connectionWasInterrupted(_: AuthenticatedChatService, error: Error?) {
XCTAssertEqual(self.stage, 3)
XCTAssertNotNil(error)
self.stage += 1
self.connectionInterrupted.fulfill()
}
@ -263,6 +264,7 @@ final class ChatServiceTests: TestCaseBase {
func chatServiceDidReceiveQueueEmpty(_: AuthenticatedChatService) {}
func chatService(_ chat: AuthenticatedChatService, didReceiveIncomingMessage envelope: Data, serverDeliveryTimestamp: UInt64, sendAck: () async throws -> Void) {}
func connectionWasInterrupted(_ service: AuthenticatedChatService, error: Error?) {}
}
let net = Net(env: .staging, userAgent: Self.userAgent)
@ -298,7 +300,8 @@ final class ChatServiceTests: TestCaseBase {
self.expectation = expectation
}
func connectionWasInterrupted(_: UnauthenticatedChatService) {
func connectionWasInterrupted(_: UnauthenticatedChatService, error: Error?) {
XCTAssertNil(error)
self.expectation.fulfill()
}
}