Repository: tipsy/javalin
Pull request #1352 (2021-08-18 14:41:39)

[sse] Make it possible to send inputstream, string, and objects


Patch:
diff --git a/javalin/src/main/java/io/javalin/http/Context.kt b/javalin/src/main/java/io/javalin/http/Context.kt
index c9278cb7..90dba74b 100644
--- a/javalin/src/main/java/io/javalin/http/Context.kt
+++ b/javalin/src/main/java/io/javalin/http/Context.kt
@@ -400,6 +400,10 @@ open class Context(@JvmField val req: HttpServletRequest, @JvmField val res: Htt
         }
     }
 
+    /** Sets the response status. */
+    fun status(httpCode: HttpCode): Context =
+        status(httpCode.status)
+
     /** Sets the response status. */
     fun status(statusCode: Int): Context {
         res.status = statusCode
diff --git a/javalin/src/main/java/io/javalin/http/sse/Emitter.kt b/javalin/src/main/java/io/javalin/http/sse/Emitter.kt
index 1df31e7e..e797401f 100644
--- a/javalin/src/main/java/io/javalin/http/sse/Emitter.kt
+++ b/javalin/src/main/java/io/javalin/http/sse/Emitter.kt
@@ -1,6 +1,7 @@
 package io.javalin.http.sse
 
 import java.io.IOException
+import java.io.InputStream
 import javax.servlet.AsyncContext
 import javax.servlet.ServletOutputStream
 
@@ -18,19 +19,15 @@ class Emitter(private var asyncContext: AsyncContext) {
         }
     }
 
-    @JvmOverloads
-    fun emit(event: String, data: String, id: String? = null) = synchronized(this) {
+    fun emit(event: String, data: InputStream, id: String?) = synchronized(this) {
         try {
-            val sb = StringBuilder()
             if (id != null) {
-                sb.append("id: $id$newline")
+                output.print("id: $id$newline")
             }
-            sb.append("event: $event$newline")
-            data.lines().forEach { line ->
-                sb.append("data: $line$newline")
-            }
-            sb.append("$newline$newline")
-            output.print(sb.toString())
+            output.print("event: $event$newline")
+            output.print("data: " )
+            data.copyTo(output)
+            output.print(newline)
             asyncContext.response.flushBuffer()
         } catch (e: IOException) {
             closed = true
diff --git a/javalin/src/main/java/io/javalin/http/sse/SseClient.kt b/javalin/src/main/java/io/javalin/http/sse/SseClient.kt
index dc1f34e2..5f4468d5 100644
--- a/javalin/src/main/java/io/javalin/http/sse/SseClient.kt
+++ b/javalin/src/main/java/io/javalin/http/sse/SseClient.kt
@@ -1,6 +1,8 @@
 package io.javalin.http.sse
 
 import io.javalin.http.Context
+import io.javalin.plugin.json.jsonMapper
+import java.io.InputStream
 
 class SseClient(@JvmField val ctx: Context) {
 
@@ -11,11 +13,15 @@ class SseClient(@JvmField val ctx: Context) {
         this.closeCallback = closeCallback
     }
 
-    fun sendEvent(data: String) = sendEvent("message", data)
+    fun sendEvent(data: Any) = sendEvent("message", data)
 
     @JvmOverloads
-    fun sendEvent(event: String, data: String, id: String? = null) {
-        emitter.emit(event, data, id)
+    fun sendEvent(event: String, data: Any, id: String? = null) {
+        when (data) {
+            is InputStream -> emitter.emit(event, data, id)
+            is String -> emitter.emit(event, data.byteInputStream(), id)
+            else -> emitter.emit(event, ctx.jsonMapper().toJsonString(data).byteInputStream(), id)
+        }
         if (emitter.isClosed()) { // can't detect if closed before we try emitting?
             closeCallback.run()
         }


Test patch:
diff --git a/javalin/src/test/java/io/javalin/TestSse.kt b/javalin/src/test/java/io/javalin/TestSse.kt
index d06297e7..569ac176 100644
--- a/javalin/src/test/java/io/javalin/TestSse.kt
+++ b/javalin/src/test/java/io/javalin/TestSse.kt
@@ -1,10 +1,10 @@
 package io.javalin
 
 import io.javalin.http.sse.SseClient
+import io.javalin.testing.SerializableObject
 import io.javalin.testing.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
-import java.util.*
 
 class TestSse {
 
@@ -20,6 +20,18 @@ class TestSse {
         assertThat(body).contains("data: $data")
     }
 
+    @Test
+    fun `sending input stream works`() = TestUtil.test(shortTimeoutServer()) { app, http ->
+        app.sse("/sse") { it.sendEvent(event, "MY DATA".byteInputStream()) }
+        assertThat(http.sse("/sse").get().body).contains("data: MY DATA")
+    }
+
+    @Test
+    fun `sending json works`() = TestUtil.test(shortTimeoutServer()) { app, http ->
+        app.sse("/sse") { it.sendEvent(event, SerializableObject()) }
+        assertThat(http.sse("/sse").get().body).contains("""data: {"value1":"FirstValue","value2":"SecondValue"}""")
+    }
+
     @Test
     fun `sending events with ID works`() = TestUtil.test(shortTimeoutServer()) { app, http ->
         app.sse("/sse") { it.sendEvent(event, data, id = "SOME_ID") }
@@ -68,5 +80,5 @@ class TestSse {
         assertThat(body).contains("event: $event")
         assertThat(body).contains("data: " + "my-qp")
     }
-    
+
 }