org.http4k.server.Jetty11EventStreamHandler.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of http4k-server-jetty11 Show documentation
Show all versions of http4k-server-jetty11 Show documentation
Http4k HTTP Server built on top of jetty v11
package org.http4k.server
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.eclipse.jetty.server.handler.HandlerWrapper
import org.http4k.core.ContentType.Companion.TEXT_EVENT_STREAM
import org.http4k.core.Headers
import org.http4k.core.Status
import org.http4k.servlet.jakarta.asHttp4kRequest
import org.http4k.sse.SseHandler
import java.nio.charset.StandardCharsets
import java.time.Duration
import org.eclipse.jetty.server.Request as JettyRequest
class Jetty11EventStreamHandler(
private val sse: SseHandler,
private val heartBeatDuration: Duration = Duration.ofSeconds(15)
) : HandlerWrapper() {
override fun handle(
target: String, baseRequest: JettyRequest,
request: HttpServletRequest, response: HttpServletResponse
) {
if (!baseRequest.isHandled && request.isEventStream()) {
val connectRequest = request.asHttp4kRequest()
if (connectRequest != null) {
val (status, headers, consumer) = sse(connectRequest)
response.writeEventStreamResponse(status, headers)
val async = request.startAsyncWithNoTimeout()
val output = async.response.outputStream
val scheduler = baseRequest.httpChannel.connector.scheduler
val server = baseRequest.httpChannel.connector.server
val emitter =
Jetty11EventStreamEmitter(connectRequest, output, heartBeatDuration, scheduler, onClose = {
async.complete()
server.removeEventListener(it)
}).also(server::addEventListener)
consumer(emitter)
baseRequest.isHandled = true
}
}
if (!baseRequest.isHandled) super.handle(target, baseRequest, request, response)
}
companion object {
private fun HttpServletRequest.isEventStream() =
method == "GET" && getHeaders("Accept").toList().any { it.contains(TEXT_EVENT_STREAM.value) }
private fun HttpServletResponse.writeEventStreamResponse(newStatus: Status, headers: Headers) {
status = newStatus.code
characterEncoding = StandardCharsets.UTF_8.name()
contentType = TEXT_EVENT_STREAM.value
// By adding this header, and not closing the connection,
// we disable HTTP chunking, and we can use write()+flush()
// to send data in the text/event-stream protocol
addHeader("Connection", "close")
headers.forEach { addHeader(it.first, it.second) }
flushBuffer()
}
private fun HttpServletRequest.startAsyncWithNoTimeout() =
startAsync().apply {
// Infinite timeout because the continuation is never resumed,
// but only completed on close
timeout = 0
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy