Signal-iOS/SignalServiceKit/src/Devices/ProvisioningSocket.swift
Jordan Rose 0d302834c8 SSK: Port OWSDeviceProvisioner (and OWSProvisioningMessage) to Swift
While here, clean up some overzealous uses of 'try!' in
OWSProvisioningCipher---this data comes from another device and
therefore can't be trusted.
2022-03-17 12:25:39 -07:00

115 lines
4.1 KiB
Swift

//
// Copyright (c) 2022 Open Whisper Systems. All rights reserved.
//
import Foundation
public protocol ProvisioningSocketDelegate: AnyObject {
func provisioningSocket(_ provisioningSocket: ProvisioningSocket, didReceiveDeviceId deviceID: String)
func provisioningSocket(_ provisioningSocket: ProvisioningSocket, didReceiveEnvelope envelope: ProvisioningProtoProvisionEnvelope)
func provisioningSocket(_ provisioningSocket: ProvisioningSocket, didError error: Error)
}
// MARK: -
// TODO: Do we still need the ProvisioningSocket if we have the unidentified websocket?
public class ProvisioningSocket {
let socket: SSKWebSocket
public weak var delegate: ProvisioningSocketDelegate?
public init() {
// TODO: Will this work with censorship circumvention?
// TODO: Should we (sometimes?) use the unidentified service?
let serviceBaseURL = URL(string: TSConstants.mainServiceWebSocketAPI_identified)!
let socketURL = URL(string: "/v1/websocket/provisioning/?agent=\(OWSDeviceProvisioner.userAgent)",
relativeTo: serviceBaseURL)!
let request = URLRequest(url: socketURL)
let websocketFactory = SSKEnvironment.shared.webSocketFactoryRef
owsAssert(websocketFactory.canBuildWebSocket)
let webSocket = websocketFactory.buildSocket(request: request,
callbackQueue: .main)!
self.socket = webSocket
webSocket.delegate = self
}
public var state: SSKWebSocketState {
return socket.state
}
public func disconnect() {
heartBeatTimer?.invalidate()
heartBeatTimer = nil
socket.disconnect()
}
var heartBeatTimer: Timer?
public func connect() {
if heartBeatTimer == nil {
heartBeatTimer = Timer.scheduledTimer(withTimeInterval: 30, repeats: true) { [weak self] _ in
guard let self = self else { return }
guard self.state == .open else { return }
self.socket.writePing()
}
}
socket.connect()
}
}
extension ProvisioningSocket: SSKWebSocketDelegate {
public func websocketDidConnect(socket: SSKWebSocket) {
Logger.debug("")
}
public func websocketDidDisconnectOrFail(socket: SSKWebSocket, error: Error?) {
if let error = error {
delegate?.provisioningSocket(self, didError: error)
} else {
Logger.debug("disconnected without error.")
}
}
public func websocket(_ socket: SSKWebSocket, didReceiveData data: Data) {
let message: WebSocketProtoWebSocketMessage
do {
message = try WebSocketProtoWebSocketMessage(serializedData: data)
} catch {
owsFailDebug("Failed to deserialize message: \(error)")
return
}
guard let request = message.request else {
owsFailDebug("unexpected message: \(message)")
return
}
do {
try handleRequest(request)
try socket.sendResponse(for: request, status: 200, message: "OK")
} catch {
owsFailDebug("error: \(error)")
}
}
private func handleRequest(_ request: WebSocketProtoWebSocketRequestMessage) throws {
Logger.debug("message: \(request.verb) \(request.path)")
switch (request.verb, request.path) {
case ("PUT", "/v1/address"):
guard let body = request.body else {
throw OWSAssertionError("body was unexpectedly nil")
}
let uuidProto = try ProvisioningProtoProvisioningUuid(serializedData: body)
delegate?.provisioningSocket(self, didReceiveDeviceId: uuidProto.uuid)
case ("PUT", "/v1/message"):
guard let body = request.body else {
throw OWSAssertionError("body was unexpectedly nil")
}
let envelopeProto = try ProvisioningProtoProvisionEnvelope(serializedData: body)
delegate?.provisioningSocket(self, didReceiveEnvelope: envelopeProto)
default:
throw OWSAssertionError("unexpected request: \(request)")
}
}
}