mateclaw/docs/compatibility/feishu-websocket-lifecycle.md
RobinZhiBin 61b7f56bca
fix(feishu): own WebSocket readiness and resource lifecycle (#659)
Contributed by RobinZhiBin <robinzhi@gmail.com> (@RobinZhiBin). Wait for handshake readiness, release transport resources, and validate liveness using protocol pings. Includes both original commits; verified by local socket and channel health regression tests.
2026-10-09 03:53:46 -04:00

53 lines
3.0 KiB
Markdown

# Feishu WebSocket lifecycle (oapi-sdk 2.7.1)
The adapter owns reconnect scheduling. SDK `autoReconnect` stays false. SDK
`start()` initiates a handshake and returns; only `awaitReady` success makes the
adapter CONNECTED. Startup failures and actual socket failures enter the existing
single exponential-backoff loop. A 15-second probe observes the SDK socket;
OkHttp protocol ping/pong (20 seconds) detects half-open transports without
business messages. A quiet channel does not imply a broken connection.
`FeishuWebSocketClient` is a deliberately narrow, version-bound compatibility
bridge. SDK 2.7.1 has a private constructor and no public resource-disposal or
transport-status APIs. Its `close()` does not shut down the executor. The bridge
validates `executor`, `httpClient`, and `conn` field types. Reads of the non-volatile
`conn` field acquire the SDK monitor used by `disconnect()`. It configures the dedicated
HTTP client before use, and releases SDK/HTTP executors and idle connections on
permanent close. It cancels the underlying socket after SDK close (which only
enqueues a graceful close), outside the SDK monitor to allow failure callbacks.
It cancels HTTP calls and interrupts the starter before taking
the lifecycle lock, so a pending handshake cannot prevent shutdown. Do not remove
these contract tests or upgrade the SDK without rechecking its lifecycle.
Feishu opts out of message-age staleness with Duration.ZERO. Other adapters retain
their existing thresholds.
ERROR recovery remains active; long-poll adapters retain their existing positive
thresholds. The legacy Feishu `silent_disconnect_threshold_seconds` setting no
longer triggers reconnects. A transport reconnect reuses the HTTP token client;
token refresh already has its own schedule. Permanent stop shuts down that client.
Regression tests use a loopback-only HTTP/WebSocket peer and synthetic credentials,
with no model calls or live Feishu messages. They cover delayed/rejected handshakes,
TCP resets, peer close, missing pongs, stop during startup, close-before-start,
close between handshake and success, and 25 real SDK start/stop cycles. Each old
SDK and HTTP executor must terminate; quiet channels, ERROR cooldown, and Weixin
five-minute stale recovery are verified separately.
## Validation
Run the lifecycle and existing channel regressions on Java 21:
```sh
JAVA_TOOL_OPTIONS=-Djdk.httpclient.allowRestrictedHeaders=connection \
mvn -pl mateclaw-server -am \
-Dtest='Feishu*Test,ChannelHealthMonitor*Test,ChannelAdapterStalenessTest' \
-Dsurefire.failIfNoSpecifiedTests=false test
```
The JVM option matches the application startup HTTP setting; the broader existing
Feishu HTTP tests also run outside the application main method. The lifecycle
tests do not need credentials or Internet access. The native SDK
resource bridge is scoped to the pinned oapi-sdk 2.7.1; review the reflected field
contracts before changing that dependency. Local socket tests do not establish
real-user delivery or multi-day reliability.