Coverage Summary for Class: VThreadServer (dev.suresh.vthread)
Class |
Class, %
|
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
VThreadServer |
0%
(0/1)
|
0%
(0/12)
|
|
0%
(0/81)
|
0%
(0/378)
|
package dev.suresh.vthread
import com.sun.net.httpserver.*
import java.lang.Thread.sleep
import java.net.*
import java.net.http.*
import java.net.http.HttpResponse.BodyHandlers
import java.time.*
import java.util.concurrent.*
import java.util.stream.Collectors.joining
import kotlin.system.*
import okhttp3.tls.internal.*
object VThreadServer {
private val execSvc = Executors.newVirtualThreadPerTaskExecutor()
@JvmStatic
fun run() {
val took = measureTimeMillis { exec() }
println(">> Took ${Duration.ofMillis(took).toSeconds()} seconds!")
}
private fun exec() {
println("Generating a self signed cert...")
val selfSignedCert = TlsUtil.localhost()
val cn = selfSignedCert.trustManager.acceptedIssuers[0].subjectX500Principal
println("Self signed cert: $cn")
// Starts HTTPS server
val httpsServer =
HttpsServer.create(InetSocketAddress(8443), 1_000).apply {
httpsConfigurator = HttpsConfigurator(selfSignedCert.sslContext())
executor = execSvc
createContext("/", ::root)
createContext("/top", ::top)
start()
}
val url = "https://localhost:${httpsServer.address.port}"
println("Started the server on $url")
val client =
HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
.sslContext(selfSignedCert.sslContext())
.version(HttpClient.Version.HTTP_2)
.executor(execSvc)
.build()
println("Sending 500 concurrent requests to $url")
val futures =
(1..500).map {
CompletableFuture.supplyAsync(
{
val res =
client.send(
HttpRequest.newBuilder().uri(URI.create(url)).build(),
BodyHandlers.ofString())
val thread = Thread.currentThread()
println(
"<--- Response(${thread.name}-${thread.threadId()}-${thread.isVirtual}): ${res.body()}")
res.body()
},
execSvc)
.exceptionally(Throwable::message)
}
// Wait for all tasks to complete and prints the response.
CompletableFuture.allOf(*futures.toTypedArray())
.handle { _, _ -> // or thenRun
// Finally stop the server.
println("Shutting down the server!")
httpsServer.stop(1)
futures.map { it.join() }
}
.thenAccept { it.forEach(::println) }
.join()
}
private fun root(ex: HttpExchange) {
val thread = Thread.currentThread()
println(
"---> Request(${thread.name}-${thread.threadId()}-${thread.isVirtual}): ${ex.requestMethod} - ${ex.requestURI}")
// Simulate blocking call.
sleep(Duration.ofMillis(100))
ex.responseHeaders.add("Content-Type", "application/json")
val res =
"""
{
"threadId" : ${thread.threadId()},
"version" : ${System.getProperty("java.vm.version")},
"virtual" : ${thread.isVirtual}
}
"""
.trimIndent()
.toByteArray()
ex.sendResponseHeaders(200, res.size.toLong())
ex.responseBody.apply {
write(res)
close()
}
}
private fun top(ex: HttpExchange) {
println("---> Request: ${ex.requestMethod} - ${ex.requestURI}")
val res =
ProcessHandle.allProcesses()
.map {
"${it.pid()} ${it.parent().map(ProcessHandle::pid).orElse(0)} ${
it.info().startInstant()
.map(Instant::toString).orElse("-")
} ${
it.info().commandLine()
.orElse("-")
} ${it.info().user().orElse("-")}"
}
.collect(joining("<br>"))
.toByteArray()
ex.responseHeaders.add("Content-Type", "text/html; charset=UTF-8")
ex.sendResponseHeaders(200, res.size.toLong())
ex.responseBody.apply {
write(res)
close()
}
}
/**
* Disable hostname verification of JDK http client.
* http://mail.openjdk.java.net/pipermail/net-dev/2018-November/011912.html
*/
private fun disableHostnameVerification() =
System.setProperty("jdk.internal.httpclient.disableHostnameVerification", "true")
}