diff --git a/server/src/main/kotlin/com/jaytux/phoebench/server/Main.kt b/server/src/main/kotlin/com/jaytux/phoebench/server/Main.kt index 31cf5d4..be7a006 100644 --- a/server/src/main/kotlin/com/jaytux/phoebench/server/Main.kt +++ b/server/src/main/kotlin/com/jaytux/phoebench/server/Main.kt @@ -185,7 +185,7 @@ fun Application.module() { cseAuth(Routes.CSE.monitor, { id, user -> transaction { accessibleProjectCSE(user, id, true) } }, - ProjectHandler::monitorSetup) { _, _, bus, event -> + ProjectHandler::monitorSetup, ProjectHandler::monitorTeardown) { _, _, bus, event -> try { when(event) { is ClientMonitorEvent.ApplicationFinished -> bus.end(ServerMonitorEvent.ApplicationEnd(Clock.System.now(), event.exitCode)) diff --git a/server/src/main/kotlin/com/jaytux/phoebench/server/handlers/Bridge.kt b/server/src/main/kotlin/com/jaytux/phoebench/server/handlers/Bridge.kt index b24a7c9..e5ae417 100644 --- a/server/src/main/kotlin/com/jaytux/phoebench/server/handlers/Bridge.kt +++ b/server/src/main/kotlin/com/jaytux/phoebench/server/handlers/Bridge.kt @@ -282,6 +282,7 @@ inline fun Route.wra api: CSERoute, noinline extra: suspend (ApplicationCall, TParams) -> TInter, noinline setup: suspend (TParams, TInter) -> TExtra, + noinline onClose: suspend (TParams, TInter, TExtra) -> Unit, noinline handler: suspend (TParams, TInter, TExtra, TEvent) -> Unit ) { webSocket(api.pattern) { @@ -292,11 +293,15 @@ inline fun Route.wra val converter = KotlinxWebsocketSerializationConverter(Json) val extra = setup(params, inter) - for(frame in incoming) { - handler(params, inter, extra, converter.deserialize(frame)) + try { + for (frame in incoming) { + handler(params, inter, extra, converter.deserialize(frame)) + } + } + catch(e: ClosedReceiveChannelException) { + onClose(params, inter, extra) } } - catch(e: ClosedReceiveChannelException) {} catch(e: RouteError.CSERouteError) { close(CloseReason(e.code, e.message ?: "Unknown error")) } @@ -310,6 +315,7 @@ inline fun Route.wrapAuthCSE api: CSERoute, noinline verifyUser: suspend (TParams, User) -> Unit, noinline setup: suspend (TParams, User) -> TExtra, + noinline onClose: suspend (TParams, User, TExtra) -> Unit, noinline handler: suspend (TParams, User, TExtra, TEvent) -> Unit ) = wrapCSE( api = api, @@ -327,28 +333,32 @@ inline fun Route.wrapAuthCSE user }, setup = setup, + onClose = onClose, handler = handler ) inline fun Route.cse( api: CSERoute, - noinline setup: suspend (TParams, Unit) -> TExtra, + noinline setup: suspend (TParams) -> TExtra, + noinline onClose: suspend (TParams, TExtra) -> Unit, noinline handler: suspend (TParams, TExtra, TEvent) -> Unit ) { if(api.elevation != Elevation.UN_AUTH) throw IllegalArgumentException("CSE ${api.pattern} can only be used with ${api.elevation}") - wrapCSE(api, { _, _ -> }, setup) { params, _, extra, event -> handler(params, extra, event) } + wrapCSE(api, { _, _ -> }, { it, _ -> setup(it) }, { p, _, extra -> onClose(p, extra) }) { params, _, extra, event -> handler(params, extra, event) } } inline fun Route.cseAuth( api: CSERoute, noinline verifyUser: suspend (TParams, User) -> Unit, - noinline setup: suspend (TParams, User) -> TExtra, noinline handler: suspend (TParams, User, TExtra, TEvent) -> Unit + noinline setup: suspend (TParams, User) -> TExtra, noinline onClose: suspend (TParams, User, TExtra) -> Unit, + noinline handler: suspend (TParams, User, TExtra, TEvent) -> Unit ) { if(api.elevation != Elevation.AUTH) throw IllegalArgumentException("CSE ${api.pattern} can only be used with ${api.elevation}") - wrapAuthCSE(api, verifyUser, setup, handler) + wrapAuthCSE(api, verifyUser, setup, onClose, handler) } inline fun Route.cseAdmin( api: CSERoute, noinline setup: suspend (TParams, User) -> TExtra, + noinline onClose: suspend (TParams, User, TExtra) -> Unit, noinline handler: suspend (TParams, User, TExtra, TEvent) -> Unit ) { if(api.elevation != Elevation.ADMIN) throw IllegalArgumentException("CSE ${api.pattern} can only be used with ${api.elevation}") @@ -356,5 +366,5 @@ inline fun Route.cseAdmin( if(!user.isAdmin) { throw RouteError.CSERouteError("Admin access required", CloseReasons.NOT_AUTHORIZED) } - }, setup, handler) + }, setup, onClose, handler) } \ No newline at end of file diff --git a/server/src/main/kotlin/com/jaytux/phoebench/server/handlers/ProjectHandler.kt b/server/src/main/kotlin/com/jaytux/phoebench/server/handlers/ProjectHandler.kt index dcbac2d..20a5d05 100644 --- a/server/src/main/kotlin/com/jaytux/phoebench/server/handlers/ProjectHandler.kt +++ b/server/src/main/kotlin/com/jaytux/phoebench/server/handlers/ProjectHandler.kt @@ -402,6 +402,14 @@ object ProjectHandler { } } + suspend fun monitorTeardown( + id: Uuid, user: User, + bus: SSEBus.MonitorSSEBus + ) { + bus.end(ServerMonitorEvent.ApplicationEnd(Clock.System.now(), -1)) + } + fun deleteMonitor(user: User, req: Uuid) = transaction { checkMigration(user) accessibleProject(user, req, true)