-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.kt
More file actions
executable file
·103 lines (88 loc) · 2.73 KB
/
server.kt
File metadata and controls
executable file
·103 lines (88 loc) · 2.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import com.sun.net.httpserver.HttpExchange
import com.sun.net.httpserver.HttpServer
import dev.datastar.kotlin.sdk.Response
import dev.datastar.kotlin.sdk.ServerSentEventGenerator
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.runBlocking
import java.io.File
import java.net.InetSocketAddress
import java.util.concurrent.Executors
///usr/bin/env jbang "$0" "$@" ; exit $?
//JAVA 21
//KOTLIN 2.2.0
//DEPS dev.cloudgt.datastar:kotlin-sdk:0.1.0
//DEPS org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2
fun main(): Unit = server().run {
start()
println("Let's go counting star... http://localhost:${address.port}")
}
fun server(
counter: MutableStateFlow<Int> = MutableStateFlow(0),
): HttpServer = HttpServer.create(
InetSocketAddress(8080),
0
).apply {
executor = Executors.newVirtualThreadPerTaskExecutor()
val counterPage = File(
"../front/counter.html"
).readBytes()
createContext("/") { exchange ->
exchange.responseHeaders.add("Content-Type", "text/html")
exchange.sendResponseHeaders(200, counterPage.size.toLong())
exchange.responseBody.use { os ->
os.write(counterPage)
}
exchange.close()
}
sseContext(path = "/counter") {
runBlocking {
counter.collect { event ->
this@sseContext.patchElements(
"""<span id="counter">${event}</span>"""
)
if (event == 3) {
this@sseContext.executeScript("""alert('Thanks for trying Datastar!')""")
}
}
}
}
createContext("/increment") { exchange ->
counter.value++
exchange.sendResponseHeaders(204, -1)
exchange.close()
}
createContext("/decrement") { exchange ->
counter.value--
exchange.sendResponseHeaders(204, -1)
exchange.close()
}
}
private fun HttpServer.sseContext(
path: String,
sender: ServerSentEventGenerator.() -> Unit
) {
createContext(path) { exchange ->
try {
val generator = ServerSentEventGenerator(adaptResponse(exchange))
sender(generator)
} finally {
exchange.close()
}
}
}
private fun adaptResponse(exchange: HttpExchange): Response =
object : Response {
override fun sendConnectionHeaders(
status: Int,
headers: Map<String, List<String>>,
) {
exchange.responseHeaders.putAll(headers)
exchange.sendResponseHeaders(status, 0)
}
override fun write(text: String) {
exchange.responseBody.write(text.toByteArray())
}
override fun flush() {
exchange.responseBody.flush()
}
}