你这个判断是对的:**Undertow 不应感知路由/中间件,Context 也不应有 `ws()`**。  
下面给你一版更干净、可落地的**完整重构方案(分层清晰版)**。

---

## 0. 设计目标(先定原则)

1. **分层职责单一**
   - `core`:路由、匹配、中间件、DI、事件、抽象连接对象
   - `server-undertow`:仅实现网络协议适配(HTTP + WS I/O),不包含业务路由逻辑

2. **HTTP 与 WS 是并列协议,不是“响应体特例”**
   - 不走 `Context.ws()` / `ResponseBody.WebSocket`

3. **统一开发体验**
   - `app.ws(...)`
   - `app.wsUse(...)`
   - `@Ws(...)`
   - 连接内可访问升级时上下文快照(path/query/state/service)

---

## 1. 分层架构(修正版)

- `colleen-core`
  - `Router`(HTTP routes + WS routes)
  - `MiddlewarePipeline`(HTTP/WS 两条链)
  - `WsRouteDefinition / WsRouteMatch`
  - `WebSocketConnection`(框架抽象)
  - `WebSocketSessionContext`(升级时上下文快照)
  - `WsEvents`

- `colleen-server-api`
  - `WebServer` 抽象
  - `WebSocketAdapter` SPI(服务器实现 WS 读写)

- `colleen-server-undertow`
  - Undertow 启动与 handler 桥接
  - `UndertowWsChannelAdapter : WebSocketAdapter`
  - 只接收 core 下发的“已匹配 WS endpoint 执行计划”,不自己匹配路由

---

## 2. 关键修正点(你提到的问题逐条落地)

### A) Undertow 不感知路由/中间件
**做法**:  
路由匹配和中间件执行全部在 core 完成。Undertow 只做:

- 识别请求(HTTP or WS upgrade)
- 把请求交给 core dispatcher
- 若 dispatcher 返回 `UpgradePlan.WebSocket`,则执行握手并绑定 channel adapter

### B) Context 不提供 `ws()`
**做法**:  
WS endpoint 由专门注册 API 进入路由表,不通过 HTTP handler 中 `ctx.ws()` 触发升级。

### C) WS 不作为 ResponseBody
**做法**:  
引入专门返回类型:

- `DispatchResult.Http(Response)`
- `DispatchResult.WebSocketUpgrade(UpgradePlan.WebSocket)`
- `DispatchResult.NotFound`

---

## 3. API 设计(对用户)

```kotlin
val app = Colleen()

app.use(RequestLogger())               // HTTP 全局中间件
app.wsUse("/ws/*", AuthWsMiddleware()) // WS 中间件链

app.ws("/ws/echo") { conn -> ... }

@Controller("/ws")
class WsController {
    @Ws("/chat/{room}")
    fun chat(conn: WebSocketConnection) { ... }
}

app.listen(8000)
```

### `wsUse` 语义
- 仅作用于 WS upgrade 请求
- 接口可复用 Middleware 签名,但执行环境是“upgrade 上下文”
- 明确禁止在其中写 HTTP body(若框架允许,也会被忽略/报错)

---

## 4. Core 数据结构(推荐)

### 4.1 Ws 路由定义
- `WsRouteDefinition(pathPattern, handler, metadata)`
- 支持 path param 提取

### 4.2 Upgrade 计划
- `UpgradePlan.WebSocket`
  - `sessionContext`(不可变快照)
  - `endpointHandler`
  - `runtimeConfig`(idle timeout / max msg size / subprotocols)

### 4.3 会话上下文(替代直接持有 Context)
- `WebSocketSessionContext`
  - `pathParams`
  - `queryParams`
  - `stateSnapshot`
  - `serviceResolver`
  - `requestMeta`(ip, headers, uri, userAgent...)

> 这样避免长期持有可变 `Context` 导致线程安全与生命周期混乱。

---

## 5. WebSocketConnection 设计(修正版)

能力:
- lifecycle: `onOpen/onMessage/onClose/onError`
- send: `sendText/sendBinary/sendJson`(可选)
- close: `close(code, reason)`
- context access:
  - `pathParam(name)`
  - `query(name)`
  - `state<T>(key)`
  - `getService<T>()`

约束:
- handler 注册阶段与事件触发阶段分离(先注册后触发 open)
- 默认多监听器 or 单监听器策略要明确(建议单监听器,重复注册报错)

---

## 6. Dispatcher 流程(核心)

1. 收到请求
2. 判断是否 WS upgrade 请求
3. 若是:
   - 路由匹配 WS route
   - 执行 `wsUse` + 全局 WS 中间件
   - 产出 `UpgradePlan.WebSocket`
4. server adapter 执行握手并绑定 channel
5. 创建 `WebSocketConnectionImpl`
6. 调用用户 endpoint(注册回调)
7. 触发 `onOpen` + `WebSocketConnected` 事件
8. 接收消息并分发 `onMessage`
9. 关闭时触发 `onClose` + `WebSocketDisconnected`

---

## 7. Undertow 适配层职责(严格边界)

Undertow 模块只包含:

- `UndertowRequestAdapter`(请求映射)
- `UndertowResponseWriter`(HTTP 写回)
- `UndertowWsChannelAdapter`(send/receive/close 映射)
- `UndertowRuntime`(线程池、配置)

**不包含**:
- 路由表
- 中间件列表
- 注解扫描
- controller 注册

---

## 8. 中间件模型(HTTP/WS 分离)

建议定义两类 pipeline:

- `HttpMiddleware`
- `WsMiddleware`

即使签名一致,也在类型上分离,防止误用。  
你可以提供桥接器,把现有通用中间件显式转换到 WS 侧。

---

## 9. 配置模型(完整)

`server { ... }` 增加:

- `wsIdleTimeoutMs`
- `wsMaxMessageSizeBytes`
- `wsMaxFrameSizeBytes`(可选)
- `wsEnableCompression`(默认 false)
- `wsAllowedOrigins`(默认空=不限制或仅同源,需明确)
- `wsHandshakeTimeoutMs`
- `wsDispatchExecutor`(io/business)

---

## 10. 事件模型

新增:
- `WebSocketConnected(sessionId, path, timestamp, attrs)`
- `WebSocketDisconnected(sessionId, code, reason, durationMs)`
- `WebSocketError(sessionId, throwable)`

事件触发线程建议统一投递到 EventBus executor,避免阻塞 IO。

---

## 11. 关键非功能设计

1. **线程模型**
   - IO 线程只收发/解码
   - 用户回调默认在业务线程池执行(可配置)

2. **背压策略**
   - 每连接发送队列上限(例如 1~10MB)
   - 溢出策略:`CLOSE | DROP_OLDEST | REJECT_SEND`

3. **大包与分片**
   - 支持聚合上限控制
   - 超限关闭(1009)

4. **错误语义**
   - 协议错误、业务异常、IO 异常区分处理与 close code 映射

---

## 12. 注解 `@Ws` 设计

- 仅在 controller 扫描阶段注册为 `WsRouteDefinition`
- 方法签名建议支持:
  - `fun(WebSocketConnection)`
  - (可选)`fun(WebSocketConnection, WsSessionContext)`  
- 不建议直接注入 HTTP Context(避免语义泄漏)

---

## 13. 文件改动建议(最终版)

### 新增(core)
- `websocket/WebSocketMessage.kt`
- `websocket/WsCloseReason.kt`
- `websocket/WebSocketConnection.kt`
- `websocket/WebSocketSessionContext.kt`
- `websocket/WsRouteDefinition.kt`
- `websocket/WsMiddleware.kt`
- `websocket/WsEvents.kt`
- `dispatch/UpgradePlan.kt`
- `dispatch/DispatchResult.kt`
- `annotation/Ws.kt`

### 修改(core)
- `Router.kt`(加入 ws route table + matcher)
- `Colleen.kt`(`ws/wsUse` 注册)
- `Dispatcher.kt`(upgrade 分支)
- `Scanner.kt`(扫描 `@Ws`)

### 新增(undertow)
- `UndertowWsChannelAdapter.kt`
- `UndertowWebSocketHandshakeHandler.kt`(仅协议桥接)

### 修改(undertow)
- `UndertowServer.kt`(根据 `DispatchResult` 执行 HTTP 或 WS)

---

## 14. 分阶段实施(建议)

- **M1**:`ws + wsUse + functional handler + text/binary + close`
- **M2**:`@Ws + path params + session context + events`
- **M3**:背压、异步发送、origin/subprotocol/compression、监控指标

---

## 15. 你之前方案中“类似问题”再补几条

- `Consumer<WebSocketConnection>` 偏 Java 风格,Kotlin DSL 可直接 `(WebSocketConnection) -> Unit`
- `attribute()`/`state()` 命名不统一
- `Context` 长生命周期引用有内存泄漏风险
- WS 与 HTTP 共用异常映射器需单独规则(握手前 vs 握手后)

---

如果你认可,我下一步可以直接给你输出两份可执行文档草案:

1. **`docs/design/websocket-architecture.md`**(架构与时序)  
2. **`docs/design/websocket-api-contract.md`**(API 契约 + 错误码 + 线程模型)

你确认后我按这版继续细化到“可直接开工”的接口级别。