mirror of
https://github.com/maxgoedjen/secretive.git
synced 2025-08-31 01:20:57 +00:00
Cleanup
This commit is contained in:
parent
07bf521414
commit
14666070e5
@ -2,65 +2,31 @@ import Foundation
|
|||||||
import OSLog
|
import OSLog
|
||||||
import SecretKit
|
import SecretKit
|
||||||
|
|
||||||
public struct Session: Sendable {
|
|
||||||
|
|
||||||
public let messages: AsyncStream<Data>
|
|
||||||
public let provenance: SigningRequestProvenance
|
|
||||||
|
|
||||||
private let fileHandle: FileHandle
|
|
||||||
private let continuation: AsyncStream<Data>.Continuation
|
|
||||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "Session")
|
|
||||||
|
|
||||||
init(fileHandle: FileHandle) {
|
|
||||||
self.fileHandle = fileHandle
|
|
||||||
provenance = SigningRequestTracer().provenance(from: fileHandle)
|
|
||||||
(messages, continuation) = AsyncStream.makeStream()
|
|
||||||
Task { [continuation, logger] in
|
|
||||||
await fileHandle.waitForDataInBackgroundAndNotifyOnMainActor()
|
|
||||||
for await _ in NotificationCenter.default.notifications(named: .NSFileHandleDataAvailable, object: fileHandle) {
|
|
||||||
let data = fileHandle.availableData
|
|
||||||
guard !data.isEmpty else {
|
|
||||||
logger.debug("Socket controller received empty data, ending continuation.")
|
|
||||||
continuation.finish()
|
|
||||||
try fileHandle.close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
continuation.yield(data)
|
|
||||||
logger.debug("Socket controller yielded data.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func write(_ data: Data) async throws {
|
|
||||||
try fileHandle.write(contentsOf: data)
|
|
||||||
await fileHandle.waitForDataInBackgroundAndNotifyOnMainActor()
|
|
||||||
}
|
|
||||||
|
|
||||||
public func close() throws {
|
|
||||||
logger.debug("Session closed.")
|
|
||||||
continuation.finish()
|
|
||||||
try fileHandle.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A controller that manages socket configuration and request dispatching.
|
/// A controller that manages socket configuration and request dispatching.
|
||||||
public struct SocketController {
|
public struct SocketController {
|
||||||
|
|
||||||
/// The active SocketPort.
|
/// A stream of Sessions. Each session represents one connection to a class communicating with the socket. Multiple Sessions may be active simultaneously.
|
||||||
|
public let sessions: AsyncStream<Session>
|
||||||
|
|
||||||
|
/// A continuation to create new sessions.
|
||||||
|
private let sessionsContinuation: AsyncStream<Session>.Continuation
|
||||||
|
|
||||||
|
/// The active SocketPort. Must be retained to be kept valid.
|
||||||
private let port: SocketPort
|
private let port: SocketPort
|
||||||
|
|
||||||
/// The FileHandle for the main socket.
|
/// The FileHandle for the main socket.
|
||||||
private let fileHandle: FileHandle
|
private let fileHandle: FileHandle
|
||||||
|
|
||||||
|
/// Logger for the socket controller.
|
||||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "SocketController")
|
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "SocketController")
|
||||||
private let requestTracer = SigningRequestTracer()
|
|
||||||
|
|
||||||
public let sessions: AsyncStream<Session>
|
/// Tracer which determines who originates a socket connection.
|
||||||
public let continuation: AsyncStream<Session>.Continuation
|
private let requestTracer = SigningRequestTracer()
|
||||||
|
|
||||||
/// Initializes a socket controller with a specified path.
|
/// Initializes a socket controller with a specified path.
|
||||||
/// - Parameter path: The path to use as a socket.
|
/// - Parameter path: The path to use as a socket.
|
||||||
public init(path: String) {
|
public init(path: String) {
|
||||||
(sessions, continuation) = AsyncStream<Session>.makeStream()
|
(sessions, sessionsContinuation) = AsyncStream<Session>.makeStream()
|
||||||
logger.debug("Socket controller setting up at \(path)")
|
logger.debug("Socket controller setting up at \(path)")
|
||||||
if let _ = try? FileManager.default.removeItem(atPath: path) {
|
if let _ = try? FileManager.default.removeItem(atPath: path) {
|
||||||
logger.debug("Socket controller removed existing socket")
|
logger.debug("Socket controller removed existing socket")
|
||||||
@ -70,12 +36,12 @@ public struct SocketController {
|
|||||||
logger.debug("Socket controller path is clear")
|
logger.debug("Socket controller path is clear")
|
||||||
port = SocketPort(path: path)
|
port = SocketPort(path: path)
|
||||||
fileHandle = FileHandle(fileDescriptor: port.socket, closeOnDealloc: true)
|
fileHandle = FileHandle(fileDescriptor: port.socket, closeOnDealloc: true)
|
||||||
Task { [fileHandle, continuation, logger] in
|
Task { [fileHandle, sessionsContinuation, logger] in
|
||||||
for await notification in NotificationCenter.default.notifications(named: .NSFileHandleConnectionAccepted) {
|
for await notification in NotificationCenter.default.notifications(named: .NSFileHandleConnectionAccepted) {
|
||||||
logger.debug("Socket controller accepted connection")
|
logger.debug("Socket controller accepted connection")
|
||||||
guard let new = notification.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else { continue }
|
guard let new = notification.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else { continue }
|
||||||
let session = Session(fileHandle: new)
|
let session = Session(fileHandle: new)
|
||||||
continuation.yield(session)
|
sessionsContinuation.yield(session)
|
||||||
await fileHandle.acceptConnectionInBackgroundAndNotifyOnMainActor()
|
await fileHandle.acceptConnectionInBackgroundAndNotifyOnMainActor()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,8 +51,68 @@ public struct SocketController {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension FileHandle {
|
extension SocketController {
|
||||||
|
|
||||||
|
/// A session represents a connection that has been established between the two ends of the socket.
|
||||||
|
public struct Session: Sendable {
|
||||||
|
|
||||||
|
/// Data received by the socket.
|
||||||
|
public let messages: AsyncStream<Data>
|
||||||
|
|
||||||
|
/// The provenance of the process that established the session.
|
||||||
|
public let provenance: SigningRequestProvenance
|
||||||
|
|
||||||
|
/// A FileHandle used to communicate with the socket.
|
||||||
|
private let fileHandle: FileHandle
|
||||||
|
|
||||||
|
/// A continuation for issuing new messages.
|
||||||
|
private let messagesContinuation: AsyncStream<Data>.Continuation
|
||||||
|
|
||||||
|
/// A logger for the session.
|
||||||
|
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "Session")
|
||||||
|
|
||||||
|
/// Initializes a new Session.
|
||||||
|
/// - Parameter fileHandle: The FileHandle used to communicate with the socket.
|
||||||
|
init(fileHandle: FileHandle) {
|
||||||
|
self.fileHandle = fileHandle
|
||||||
|
provenance = SigningRequestTracer().provenance(from: fileHandle)
|
||||||
|
(messages, messagesContinuation) = AsyncStream.makeStream()
|
||||||
|
Task { [messagesContinuation, logger] in
|
||||||
|
await fileHandle.waitForDataInBackgroundAndNotifyOnMainActor()
|
||||||
|
for await _ in NotificationCenter.default.notifications(named: .NSFileHandleDataAvailable, object: fileHandle) {
|
||||||
|
let data = fileHandle.availableData
|
||||||
|
guard !data.isEmpty else {
|
||||||
|
logger.debug("Socket controller received empty data, ending continuation.")
|
||||||
|
messagesContinuation.finish()
|
||||||
|
try fileHandle.close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
messagesContinuation.yield(data)
|
||||||
|
logger.debug("Socket controller yielded data.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes new data to the socket.
|
||||||
|
/// - Parameter data: The data to write.
|
||||||
|
public func write(_ data: Data) async throws {
|
||||||
|
try fileHandle.write(contentsOf: data)
|
||||||
|
await fileHandle.waitForDataInBackgroundAndNotifyOnMainActor()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Closes the socket and cleans up resources.
|
||||||
|
public func close() throws {
|
||||||
|
logger.debug("Session closed.")
|
||||||
|
messagesContinuation.finish()
|
||||||
|
try fileHandle.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension FileHandle {
|
||||||
|
|
||||||
/// Ensures waitForDataInBackgroundAndNotify will be called on the main actor.
|
/// Ensures waitForDataInBackgroundAndNotify will be called on the main actor.
|
||||||
@MainActor func waitForDataInBackgroundAndNotifyOnMainActor() {
|
@MainActor func waitForDataInBackgroundAndNotifyOnMainActor() {
|
||||||
waitForDataInBackgroundAndNotify()
|
waitForDataInBackgroundAndNotify()
|
||||||
@ -101,7 +127,7 @@ extension FileHandle {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SocketPort {
|
private extension SocketPort {
|
||||||
|
|
||||||
convenience init(path: String) {
|
convenience init(path: String) {
|
||||||
var addr = sockaddr_un()
|
var addr = sockaddr_un()
|
||||||
|
Loading…
Reference in New Issue
Block a user