3 files changed
+54
No files matched your search
@@ -354,6 +354,14 @@ object HermesWsClient {
|
|||||||
wsScope.launch {
|
wsScope.launch {
|
||||||
events.collect { event ->
|
events.collect { event ->
|
||||||
if (event is WsEvent.GatewayReady) {
|
if (event is WsEvent.GatewayReady) {
|
||||||
|
// Advertise support for gateway server→client requests (issue #1197).
|
||||||
|
// Older gateways may reject this method; that is harmless.
|
||||||
|
runCatching {
|
||||||
|
send(
|
||||||
|
WsMethods.CLIENT_CAPABILITIES,
|
||||||
|
mapOf("server_requests" to true),
|
||||||
|
)
|
||||||
|
}
|
||||||
val epoch = event.data?.get("replay_epoch") as? String
|
val epoch = event.data?.get("replay_epoch") as? String
|
||||||
if (!epoch.isNullOrEmpty()) {
|
if (!epoch.isNullOrEmpty()) {
|
||||||
if (replayEpoch != null && replayEpoch != epoch) {
|
if (replayEpoch != null && replayEpoch != epoch) {
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ object WsMethods {
|
|||||||
|
|
||||||
/** Inline WebSocket read-loop ping, bypasses server thread pool dispatch. */
|
/** Inline WebSocket read-loop ping, bypasses server thread pool dispatch. */
|
||||||
const val GATEWAY_PING = "gateway.ping"
|
const val GATEWAY_PING = "gateway.ping"
|
||||||
|
const val CLIENT_CAPABILITIES = "client.capabilities"
|
||||||
|
|
||||||
// ── Commands catalog ──────────────────────────────────────────────────
|
// ── Commands catalog ──────────────────────────────────────────────────
|
||||||
const val COMMANDS_CATALOG = "commands.catalog"
|
const val COMMANDS_CATALOG = "commands.catalog"
|
||||||
|
|||||||
@@ -176,6 +176,51 @@ class HermesWsClientTest {
|
|||||||
assertTrue(msg.contains(id))
|
assertTrue(msg.contains(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun gatewayReadyAdvertisesServerRequestCapability() {
|
||||||
|
val capabilityLatch = CountDownLatch(1)
|
||||||
|
var capabilityFrame: String? = null
|
||||||
|
|
||||||
|
mockWebServer.enqueue(
|
||||||
|
MockResponse().withWebSocketUpgrade(
|
||||||
|
object : WebSocketListener() {
|
||||||
|
override fun onOpen(
|
||||||
|
webSocket: WebSocket,
|
||||||
|
response: okhttp3.Response,
|
||||||
|
) {
|
||||||
|
webSocket.send(
|
||||||
|
"""{"jsonrpc":"2.0","method":"event","params":{"type":"gateway.ready","payload":{}}}""",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMessage(
|
||||||
|
webSocket: WebSocket,
|
||||||
|
text: String,
|
||||||
|
) {
|
||||||
|
val frame = Json.parseToJsonElement(text).jsonObject
|
||||||
|
if (frame["method"]?.jsonPrimitive?.content == WsMethods.CLIENT_CAPABILITIES) {
|
||||||
|
capabilityFrame = text
|
||||||
|
capabilityLatch.countDown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
HermesWsClient.connect()
|
||||||
|
|
||||||
|
assertTrue("Capability advertisement not sent", capabilityLatch.await(5, TimeUnit.SECONDS))
|
||||||
|
val frame = Json.parseToJsonElement(capabilityFrame ?: "{}").jsonObject
|
||||||
|
assertEquals(WsMethods.CLIENT_CAPABILITIES, frame["method"]?.jsonPrimitive?.content)
|
||||||
|
assertTrue(
|
||||||
|
frame["params"]
|
||||||
|
?.jsonObject
|
||||||
|
?.get("server_requests")
|
||||||
|
?.jsonPrimitive
|
||||||
|
?.content == "true",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testOpenRequestsReplayUsesTheLiveServerRequestDispatcher() =
|
fun testOpenRequestsReplayUsesTheLiveServerRequestDispatcher() =
|
||||||
runBlocking {
|
runBlocking {
|
||||||
|
|||||||
Reference in new issue
Block a user