With Armeria's support, Thorium is able to serve server-sent events. A sample of how to serve server-sent events is available in the tutorial.
First, we should create a model of the event like so:
import com.linecorp.armeria.common.sse.ServerSentEvent
import java.time.{Duration, LocalDateTime}
case class TimestampSSE(dt: LocalDateTime, event: String) extends ServerSentEvent {
override def retry(): Duration = null
override val id: String = scala.util.Random.nextLong.toHexString
override def comment(): String = null
override def data(): String = dt.toString
}
For simplicity, the controller will have two methods, one for the client to listen for incoming messages, and the other to push messages.
Take note that in this example, whenever a client is connected, 5 ping messages will automatically be pushed. Additionally, one message is pushed whenever the route /sse/sendEvent is accessed.
import com.greenfossil.thorium.Request
import com.linecorp.armeria.common.ResponseHeaders
import com.linecorp.armeria.server.annotation.Get
import com.linecorp.armeria.server.streaming.ServerSentEvents
import model.TimestampSSE
import reactor.core.publisher.Sinks
import java.time.LocalDateTime
import java.util.concurrent.CompletableFuture
object SSEController {
val sink = Sinks.many().multicast().directAllOrNothing[TimestampSSE]()
val flux = sink.asFlux()
@Get("/sse/subscribe")
def subscribeToSSE(using request: Request) = {
ServerSentEvents
.fromPublisher(
ResponseHeaders.of(200),
flux.doOnSubscribe{_ =>
// send 5 ping messages each time a client is connected
CompletableFuture.runAsync(() =>
(1 to 5).foreach{_ =>
sink.tryEmitNext(TimestampSSE(LocalDateTime.now(), "ping"))
Thread.sleep(1000)
}
)
}
)
}
@Get("/sse/sendEvent")
def sendEvent(using request: Request) = {
val event = TimestampSSE(LocalDateTime.now(), "user-initiated")
sink.tryEmitNext(event)
s"Event Id: ${event.id}"
}
}
curl -v --http2 http://localhost:8080/sse/subscribe
curl -v http://localhost:8080/sse/sendEvent