proto-socket/agent-task/archive/2026/05/kotlin_wss_crosstest/plan_0.log

352 lines
11 KiB
Text

<!-- task=kotlin_wss_crosstest plan=0 tag=KOTLIN_WSS_CROSSTEST -->
# Kotlin Server WSS Crosstest 추가
## 이 파일을 읽는 구현 에이전트에게
각 항목의 체크리스트를 완료 후 `[x]`로 표시한다.
중간 검증 명령을 실제로 실행하고 출력을 `CODE_REVIEW.md`의 검증 결과 섹션에 붙여 넣는다.
계획과 다르게 구현한 부분은 반드시 `CODE_REVIEW.md`의 "계획 대비 변경 사항"에 이유와 함께 기록한다.
## 배경
`kotlin_wss_tls` task에서 `WsServer`에 `sslContext` 파라미터를 추가해 WSS를 지원하게 됐다.
그러나 Kotlin server orchestrator 4개(`kotlin_dart.kt`, `kotlin_go.kt`, `kotlin_python.kt`, `kotlin_typescript.kt`)는
TLS TCP phase만 추가됐고 WSS phase는 누락된 상태다.
4개 client helper(`dart_kotlin_client.dart`, `go_kotlin_client Main.kt`, `python_kotlin_client.py`, `typescript_kotlin_client.ts`)는
모두 `"wss"` case가 이미 구현되어 있으므로 orchestrator쪽만 수정하면 된다.
## 포트 배정
| 파일 | TCP | WS | TLS TCP | WSS (신규) |
|------|-----|----|---------|-----------|
| kotlin_dart.kt | 29490 | 29492 | 29494 | **29496** |
| kotlin_go.kt | 29390 | 29392 | 29394 | **29396** |
| kotlin_python.kt | 29394 | 29396 | 29398 | **29400** |
| kotlin_typescript.kt | 29794 | 29796 | 29798 | **29800** |
각 WSS 포트는 해당 파일 내 기존 포트와 겹치지 않는다. 타 파일과 동일 포트가 있어도 orchestrator는 순차 실행이므로 충돌 없음.
---
## [KOTLIN_WSS_CROSSTEST-1] kotlin_dart.kt — WSS phase 추가
### 문제
`kotlin/crosstest/kotlin_dart.kt`의 `main()`이 `runTlsTcp()` 호출 후 바로 PASS를 출력한다.
WSS (kotlin WsServer + dart `"wss"` client) phase가 없다.
### 해결 방법
1. `WSS_PORT = 29496` 상수 추가 (line 37 다음)
2. `main()`에 `runWss()` 호출 뒤 `runTlsTcp()` 전에 `runWss()` 사이에 `runWss()` 실행 후 WSS phase를 호출한다.
정확히는 `runTlsTcp()` 다음, PASS 출력 전에 `runWss()` 대신 별도 `runWss()` WSS 함수를 추가한다.
3. `runWss()`는 기존 WS 패턴과 동일하게 단일 `WsServer` 인스턴스를 유지하고 `sslContext = serverCtx`를 전달한다.
4. `runWssSendPush` / `runWssRequests` 2개 내부 함수 추가.
#### Before (kotlin_dart.kt:42-57)
```kotlin
fun main() = runBlocking {
println("INFO typeName kotlin=${typeNameOf<TestData>()}")
try {
runTcpSendPush()
delay(150)
runTcpRequests()
delay(150)
runWs()
delay(150)
runTlsTcp()
println("PASS all kotlin-server/dart-client crosstests passed")
} catch (error: Throwable) {
System.err.println("FAIL crosstest error=${error.message ?: error}")
exitProcess(1)
}
}
```
#### After
```kotlin
fun main() = runBlocking {
println("INFO typeName kotlin=${typeNameOf<TestData>()}")
try {
runTcpSendPush()
delay(150)
runTcpRequests()
delay(150)
runWs()
delay(150)
runTlsTcp()
delay(150)
runWss()
println("PASS all kotlin-server/dart-client crosstests passed")
} catch (error: Throwable) {
System.err.println("FAIL crosstest error=${error.message ?: error}")
exitProcess(1)
}
}
```
#### Before (kotlin_dart.kt:37)
```kotlin
private const val TLS_TCP_PORT = 29494
```
#### After
```kotlin
private const val TLS_TCP_PORT = 29494
private const val WSS_PORT = 29496
```
#### 추가할 함수 (runTlsTcp() 이후에 삽입)
```kotlin
private suspend fun runWss() = coroutineScope {
val certFile = kotlinResourceFile("server.crt")
val keyFile = kotlinResourceFile("server.key")
val serverCtx = buildServerSslContext(certFile.path, keyFile.path)
val server = WsServer(HOST, WSS_PORT, WS_PATH, sslContext = serverCtx) { conn ->
WsClient.forServer(conn, 0, 0, parserMap())
}
server.start()
delay(100)
try {
runWssSendPush(server, certFile.path)
delay(150)
runWssRequests(server, certFile.path)
} finally {
server.stop()
}
}
private suspend fun runWssSendPush(server: WsServer, certPath: String) = coroutineScope {
val received = CompletableDeferred<Boolean>()
server.onClientConnected = { client ->
addListenerTyped<TestData>(client.communicator) { data ->
println("SERVER_RECEIVED index=${data.index} message=${data.message}")
val valid = data.index == 101 && data.message == "fire from dart client"
received.complete(valid)
if (valid) {
launch {
client.send(
TestData.newBuilder()
.setIndex(200)
.setMessage("push from kotlin server")
.build(),
)
}
}
}
}
runDartClient("wss", WSS_PORT, "send-push", setOf("1", "2"), certPath)
val ok = withTimeoutOrNull(SERVER_OBSERVATION_MS) { received.await() } ?: false
check(ok) { "WSS send-push server did not receive expected data" }
}
private suspend fun runWssRequests(server: WsServer, certPath: String) {
server.onClientConnected = { client ->
addRequestListenerTyped<TestData, TestData>(client.communicator) { req ->
TestData.newBuilder()
.setIndex(req.index * 2)
.setMessage("echo: ${req.message}")
.build()
}
}
runDartClient("wss", WSS_PORT, "requests", setOf("3", "4"), certPath)
}
```
### 수정 파일 및 체크리스트
- [x] `kotlin/crosstest/kotlin_dart.kt`
- [x] `WSS_PORT = 29496` 상수 추가
- [x] `main()`에 `delay(150)` + `runWss()` 추가 (runTlsTcp() 뒤)
- [x] `runWss()`, `runWssSendPush()`, `runWssRequests()` 함수 추가
### 테스트 작성
신규 파일 불필요. orchestrator 자체가 e2e 검증이다.
### 중간 검증
```bash
$ cd kotlin && env JAVA_HOME=/config/opt/jdk/jdk-17.0.10+7 GRADLE_USER_HOME=/tmp/gradle ./gradlew compileCrosstestKotlin
# BUILD SUCCESSFUL
```
---
## [KOTLIN_WSS_CROSSTEST-2] kotlin_go.kt — WSS phase 추가
### 문제
`kotlin/crosstest/kotlin_go.kt`에 WSS phase 없음. `go_kotlin_client` Main.kt에는 `"wss"` case 구현됨.
### 해결 방법
`kotlin_dart.kt`와 동일한 패턴. 차이점:
- `WSS_PORT = 29396`
- 클라이언트 runner: `runGoClient("wss", WSS_PORT, ...)` (이미 존재하는 함수)
#### Before (kotlin_go.kt:37)
```kotlin
private const val TLS_TCP_PORT = 29394
```
#### After
```kotlin
private const val TLS_TCP_PORT = 29394
private const val WSS_PORT = 29396
```
`main()` 변경, `runWss()` / `runWssSendPush()` / `runWssRequests()` 추가는 kotlin_dart.kt와 동일 패턴.
`runGoClient("wss", ...)` 호출.
### 수정 파일 및 체크리스트
- [x] `kotlin/crosstest/kotlin_go.kt`
- [x] `WSS_PORT = 29396` 상수 추가
- [x] `main()`에 `delay(150)` + `runWss()` 추가
- [x] `runWss()`, `runWssSendPush()`, `runWssRequests()` 함수 추가 (Go client 호출)
### 테스트 작성
orchestrator e2e로 충분.
### 중간 검증
```bash
$ cd kotlin && env JAVA_HOME=/config/opt/jdk/jdk-17.0.10+7 GRADLE_USER_HOME=/tmp/gradle ./gradlew compileCrosstestKotlin
# BUILD SUCCESSFUL
```
---
## [KOTLIN_WSS_CROSSTEST-3] kotlin_python.kt — WSS phase 추가
### 문제
`kotlin/crosstest/kotlin_python.kt`에 WSS phase 없음. `python_kotlin_client.py`에는 `"wss"` case 구현됨.
### 해결 방법
동일 패턴. 차이점:
- `WSS_PORT = 29400`
- 클라이언트 runner: `runPythonClient("wss", WSS_PORT, ...)`
#### Before (kotlin_python.kt:37)
```kotlin
private const val TLS_TCP_PORT = 29398
```
#### After
```kotlin
private const val TLS_TCP_PORT = 29398
private const val WSS_PORT = 29400
```
### 수정 파일 및 체크리스트
- [x] `kotlin/crosstest/kotlin_python.kt`
- [x] `WSS_PORT = 29400` 상수 추가
- [x] `main()`에 `delay(150)` + `runWss()` 추가
- [x] `runWss()`, `runWssSendPush()`, `runWssRequests()` 함수 추가 (Python client 호출)
### 테스트 작성
orchestrator e2e로 충분.
### 중간 검증
```bash
$ cd kotlin && env JAVA_HOME=/config/opt/jdk/jdk-17.0.10+7 GRADLE_USER_HOME=/tmp/gradle ./gradlew compileCrosstestKotlin
# BUILD SUCCESSFUL
```
---
## [KOTLIN_WSS_CROSSTEST-4] kotlin_typescript.kt — WSS phase 추가
### 문제
`kotlin/crosstest/kotlin_typescript.kt`에 WSS phase 없음. `typescript_kotlin_client.ts`에는 `"wss"` case 구현됨.
### 해결 방법
동일 패턴. 차이점:
- `WSS_PORT = 29800`
- 클라이언트 runner: `runTypescriptClient("wss", WSS_PORT, ...)`
#### Before (kotlin_typescript.kt:37)
```kotlin
private const val TLS_TCP_PORT = 29798
```
#### After
```kotlin
private const val TLS_TCP_PORT = 29798
private const val WSS_PORT = 29800
```
### 수정 파일 및 체크리스트
- [x] `kotlin/crosstest/kotlin_typescript.kt`
- [x] `WSS_PORT = 29800` 상수 추가
- [x] `main()`에 `delay(150)` + `runWss()` 추가
- [x] `runWss()`, `runWssSendPush()`, `runWssRequests()` 함수 추가 (TypeScript client 호출)
### 테스트 작성
orchestrator e2e로 충분.
### 중간 검증
```bash
$ cd kotlin && env JAVA_HOME=/config/opt/jdk/jdk-17.0.10+7 GRADLE_USER_HOME=/tmp/gradle ./gradlew compileCrosstestKotlin
# BUILD SUCCESSFUL
```
---
## 의존 관계 및 구현 순서
1-4번은 서로 독립적이나 중간 검증을 공유하므로 4개 모두 수정 후 한 번에 컴파일 검증.
## 수정 파일 요약
| 파일 | 항목 |
|------|------|
| `kotlin/crosstest/kotlin_dart.kt` | KOTLIN_WSS_CROSSTEST-1 |
| `kotlin/crosstest/kotlin_go.kt` | KOTLIN_WSS_CROSSTEST-2 |
| `kotlin/crosstest/kotlin_python.kt` | KOTLIN_WSS_CROSSTEST-3 |
| `kotlin/crosstest/kotlin_typescript.kt` | KOTLIN_WSS_CROSSTEST-4 |
## 최종 검증
```bash
$ cd kotlin && env JAVA_HOME=/config/opt/jdk/jdk-17.0.10+7 GRADLE_USER_HOME=/tmp/gradle \
./gradlew run -PmainClass=com.tokilabs.toki_socket.crosstest.KotlinDartKt
# PASS all kotlin-server/dart-client crosstests passed
$ cd kotlin && env PATH=/config/go-sdk/go/bin:$PATH JAVA_HOME=/config/opt/jdk/jdk-17.0.10+7 \
GRADLE_USER_HOME=/tmp/gradle GOCACHE=/tmp/go-build GOMODCACHE=/tmp/go-mod \
./gradlew run -PmainClass=com.tokilabs.toki_socket.crosstest.KotlinGoKt
# PASS all kotlin-server/go-client crosstests passed
$ cd kotlin && env JAVA_HOME=/config/opt/jdk/jdk-17.0.10+7 GRADLE_USER_HOME=/tmp/gradle \
./gradlew run -PmainClass=com.tokilabs.toki_socket.crosstest.KotlinPythonKt
# PASS all kotlin-server/python-client crosstests passed
$ cd kotlin && env JAVA_HOME=/config/opt/jdk/jdk-17.0.10+7 GRADLE_USER_HOME=/tmp/gradle \
./gradlew run -PmainClass=com.tokilabs.toki_socket.crosstest.KotlinTypescriptKt
# PASS all kotlin-server/typescript-client crosstests passed
```