initial commit
This commit is contained in:
90
ios_client/EncryptedChat/Network/ProtocolHandler.swift
Normal file
90
ios_client/EncryptedChat/Network/ProtocolHandler.swift
Normal file
@@ -0,0 +1,90 @@
|
||||
import Foundation
|
||||
|
||||
/// Newline-delimited JSON protocol handler.
|
||||
/// Matches Python: protocol.py build_request, build_response, parse_message, encode_binary, decode_binary
|
||||
enum ProtocolHandler {
|
||||
|
||||
/// Build a request message (newline-terminated JSON).
|
||||
/// Matches Python: build_request(msg_type, request_id=None, **kwargs)
|
||||
static func buildRequest(type: String, requestId: String? = nil, params: [String: Any] = [:]) throws -> Data {
|
||||
var msg: [String: Any] = ["type": type]
|
||||
if let requestId = requestId {
|
||||
msg["request_id"] = requestId
|
||||
}
|
||||
// Merge params into msg
|
||||
for (key, value) in params {
|
||||
msg[key] = value
|
||||
}
|
||||
|
||||
let jsonData = try JSONSerialization.data(withJSONObject: msg)
|
||||
guard jsonData.count < Constants.maxMessageBytes else {
|
||||
throw NetworkError.messageTooLarge
|
||||
}
|
||||
return jsonData + Data([0x0A]) // newline
|
||||
}
|
||||
|
||||
/// Build a response message (newline-terminated JSON).
|
||||
static func buildResponse(type: String, status: String, data: [String: Any]? = nil, requestId: String? = nil) throws -> Data {
|
||||
var msg: [String: Any] = ["type": type, "status": status]
|
||||
if let data = data {
|
||||
msg["data"] = data
|
||||
}
|
||||
if let requestId = requestId {
|
||||
msg["request_id"] = requestId
|
||||
}
|
||||
|
||||
let jsonData = try JSONSerialization.data(withJSONObject: msg)
|
||||
guard jsonData.count < Constants.maxMessageBytes else {
|
||||
throw NetworkError.messageTooLarge
|
||||
}
|
||||
return jsonData + Data([0x0A])
|
||||
}
|
||||
|
||||
/// Parse a single protocol message from bytes.
|
||||
/// Matches Python: parse_message(line)
|
||||
static func parseMessage(_ data: Data) throws -> [String: Any] {
|
||||
let trimmed = data.trimmingNewlines()
|
||||
guard !trimmed.isEmpty else {
|
||||
throw NetworkError.protocolError("Empty message")
|
||||
}
|
||||
guard let obj = try JSONSerialization.jsonObject(with: trimmed) as? [String: Any] else {
|
||||
throw NetworkError.protocolError("Message is not a JSON object")
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
/// Encode bytes to base64 string.
|
||||
/// Matches Python: encode_binary(data)
|
||||
static func encodeBinary(_ data: Data) -> String {
|
||||
data.base64EncodedString(options: [])
|
||||
}
|
||||
|
||||
/// Decode base64 string to bytes.
|
||||
/// Matches Python: decode_binary(data)
|
||||
static func decodeBinary(_ string: String) throws -> Data {
|
||||
guard let data = Data(base64Encoded: string, options: .ignoreUnknownCharacters) else {
|
||||
throw CryptoError.invalidBase64
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
/// Generate a new request ID (UUID string).
|
||||
static func newRequestId() -> String {
|
||||
UUID().uuidString
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Data Helpers
|
||||
|
||||
private extension Data {
|
||||
func trimmingNewlines() -> Data {
|
||||
var data = self
|
||||
while let last = data.last, last == 0x0A || last == 0x0D {
|
||||
data.removeLast()
|
||||
}
|
||||
while let first = data.first, first == 0x0A || first == 0x0D {
|
||||
data.removeFirst()
|
||||
}
|
||||
return data
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user