gateway: enforce hello order and modern json
parent
c41b506741
commit
ab9b12e883
|
|
@ -202,16 +202,16 @@ private actor GatewayChannelActor {
|
||||||
throw self.wrap(error, context: "gateway connect")
|
throw self.wrap(error, context: "gateway connect")
|
||||||
}
|
}
|
||||||
let id = UUID().uuidString
|
let id = UUID().uuidString
|
||||||
let paramsObject = params?.reduce(into: [String: Any]()) { dict, entry in
|
// Encode request using the generated models to avoid JSONSerialization/ObjC bridging pitfalls.
|
||||||
dict[entry.key] = entry.value.value
|
let paramsObject = params?.reduce(into: [String: AnyCodable]()) { dict, entry in
|
||||||
} ?? [:]
|
dict[entry.key] = entry.value
|
||||||
let frame: [String: Any] = [
|
}
|
||||||
"type": "req",
|
let frame = RequestFrame(
|
||||||
"id": id,
|
type: "req",
|
||||||
"method": method,
|
id: id,
|
||||||
"params": paramsObject,
|
method: method,
|
||||||
]
|
params: paramsObject.map { AnyCodable($0) })
|
||||||
let data = try JSONSerialization.data(withJSONObject: frame)
|
let data = try self.encoder.encode(frame)
|
||||||
let response = try await withCheckedThrowingContinuation { (cont: CheckedContinuation<GatewayFrame, Error>) in
|
let response = try await withCheckedThrowingContinuation { (cont: CheckedContinuation<GatewayFrame, Error>) in
|
||||||
self.pending[id] = cont
|
self.pending[id] = cont
|
||||||
Task {
|
Task {
|
||||||
|
|
@ -230,13 +230,11 @@ private actor GatewayChannelActor {
|
||||||
let msg = (res.error?["message"]?.value as? String) ?? "gateway error"
|
let msg = (res.error?["message"]?.value as? String) ?? "gateway error"
|
||||||
throw NSError(domain: "Gateway", code: 3, userInfo: [NSLocalizedDescriptionKey: msg])
|
throw NSError(domain: "Gateway", code: 3, userInfo: [NSLocalizedDescriptionKey: msg])
|
||||||
}
|
}
|
||||||
if let payload = res.payload?.value {
|
if let payload = res.payload {
|
||||||
if JSONSerialization.isValidJSONObject(payload) {
|
// Encode back to JSON with Swift's encoder to preserve types and avoid ObjC bridging exceptions.
|
||||||
let payloadData = try JSONSerialization.data(withJSONObject: payload)
|
return try self.encoder.encode(payload)
|
||||||
return payloadData
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return Data()
|
return Data() // Should not happen, but tolerate empty payloads.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap low-level URLSession/WebSocket errors with context so UI can surface them.
|
// Wrap low-level URLSession/WebSocket errors with context so UI can surface them.
|
||||||
|
|
|
||||||
|
|
@ -312,8 +312,6 @@ export async function startGatewayServer(port = 18789): Promise<GatewayServer> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
client = { socket, hello, connId };
|
|
||||||
clients.add(client);
|
|
||||||
// synthesize presence entry for this connection (client fingerprint)
|
// synthesize presence entry for this connection (client fingerprint)
|
||||||
const presenceKey = hello.client.instanceId || connId;
|
const presenceKey = hello.client.instanceId || connId;
|
||||||
const remoteAddr = (
|
const remoteAddr = (
|
||||||
|
|
@ -354,7 +352,11 @@ export async function startGatewayServer(port = 18789): Promise<GatewayServer> {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
clearTimeout(handshakeTimer);
|
clearTimeout(handshakeTimer);
|
||||||
|
// Add the client only after the hello response is ready so no tick/presence
|
||||||
|
// events reach it before the handshake completes.
|
||||||
|
client = { socket, hello, connId };
|
||||||
send(helloOk);
|
send(helloOk);
|
||||||
|
clients.add(client);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue