-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
KTOR-7908 Add heartbeat to SSE #4543
KTOR-7908 Add heartbeat to SSE #4543
Conversation
7fce919
to
32ed5b0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple a minor things, looks nice 👍
@Test | ||
fun testHeartbeat() = testApplication { | ||
install(SSE) | ||
routing { | ||
sse { | ||
heartbeat { | ||
duration = 100.milliseconds | ||
event = ServerSentEvent("heartbeat") | ||
} | ||
|
||
send("Hello") | ||
delay(200.milliseconds) | ||
} | ||
} | ||
|
||
val client = createSseClient() | ||
val events = mutableListOf<ServerSentEvent>() | ||
client.sse { | ||
incoming.collect { events.add(it) } | ||
} | ||
assertTrue { events.size > 1 } | ||
assertTrue { events.filter { it.data == "Hello" }.size == 1 } | ||
assertTrue { events.any { it.data == "heartbeat" } } | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe instead of using delays and asserts here, you could use some async processing and count a few of each to cover receiving multiple...
Something like:
@Test
fun testHeartbeat() = testApplication {
install(SSE)
routing {
sse {
heartbeat {
duration = 10.milliseconds
event = ServerSentEvent("heartbeat")
}
while(true) {
send("Hello")
delay(10.milliseconds)
}
}
}
val client = createSseClient()
var hellos = 0
var heartbeats = 0
withTimeout(1_000) {
client.sse {
incoming.collect { event ->
when(event.data) {
"Hello" -> hellos++
"heartbeat" -> heartbeats++
}
if (hellos > 3 && heartbeats > 3)
cancel()
}
}
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks better, thanks!
*/ | ||
public fun ServerSSESession.heartbeat(heartbeatConfig: Heartbeat.() -> Unit) { | ||
val heartbeat = Heartbeat().apply(heartbeatConfig) | ||
val heartbeatJob = Job() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you use Job(call.coroutineContext[Job])
here to attach it to the call?
public fun ServerSSESession.heartbeat(heartbeatConfig: Heartbeat.() -> Unit) { | ||
val heartbeat = Heartbeat().apply(heartbeatConfig) | ||
val heartbeatJob = Job() | ||
launch(heartbeatJob) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wondering why we don't add a CoroutineName
to launched coroutines? In theory, this would simplify further debugging. Both, for us and for users.
@marychatte, @bjhham, WDYT?
f8ee92a
to
2206098
Compare
bc4a92e
to
5d495eb
Compare
d50a66e
to
cc3f8f8
Compare
5d495eb
to
e0e75db
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know it's a bit late, but I left a coupe of comments 😅
* | ||
* @param heartbeatConfig a lambda that configures the [Heartbeat] object used for the heartbeat. | ||
*/ | ||
public fun ServerSSESession.heartbeat(heartbeatConfig: Heartbeat.() -> Unit) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we add a default value here?
public fun ServerSSESession.heartbeat(heartbeatConfig: Heartbeat.() -> Unit) { | |
public fun ServerSSESession.heartbeat(heartbeatConfig: Heartbeat.() -> Unit = {}) { |
* @property event the [ServerSentEvent] to be sent as the heartbeat, default is a [ServerSentEvent] with the comment "heartbeat". | ||
*/ | ||
public class Heartbeat { | ||
public var duration: Duration = 30.seconds |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably period
would be better?
Subsystem
Server, SSE
Motivation
KTOR-7908