package moe.nea.firmament.util.net import java.io.InputStream import java.net.URI import java.net.URL import java.net.http.HttpClient import java.net.http.HttpRequest import java.net.http.HttpResponse import java.nio.ByteBuffer import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletionStage import java.util.concurrent.Flow import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.json.decodeFromStream import kotlinx.serialization.serializer import moe.nea.firmament.Firmament object HttpUtil { val httpClient = HttpClient.newBuilder() .followRedirects(HttpClient.Redirect.NORMAL) .build() data class Request(val request: HttpRequest.Builder) { fun execute(bodyHandler: HttpResponse.BodyHandler): CompletableFuture> { return httpClient.sendAsync(request.build(), bodyHandler) } fun forBody(bodyHandler: HttpResponse.BodyHandler): CompletableFuture { return execute(bodyHandler).thenApply { it.body() } } fun forInputStream(): CompletableFuture { return forBody(HttpResponse.BodyHandlers.ofInputStream()) } inline fun forJson(): CompletableFuture { return forJson(serializer()) } fun forJson(serializer: DeserializationStrategy): CompletableFuture { return forBody(jsonBodyHandler(serializer)) } fun header(key: String, value: String) { request.header(key, value) } } fun jsonBodyHandler(serializer: DeserializationStrategy): HttpResponse.BodyHandler { val inp = HttpResponse.BodyHandlers.ofInputStream() return HttpResponse.BodyHandler { val subscriber = inp.apply(it) object : HttpResponse.BodySubscriber { override fun getBody(): CompletionStage { return subscriber.body.thenApply { Firmament.json.decodeFromStream(serializer, it) } } override fun onSubscribe(subscription: Flow.Subscription?) { subscriber.onSubscribe(subscription) } override fun onNext(item: List?) { subscriber.onNext(item) } override fun onError(throwable: Throwable?) { subscriber.onError(throwable) } override fun onComplete() { subscriber.onComplete() } } } } fun request(url: String): Request = request(URI.create(url)) fun request(url: URL): Request = request(url.toURI()) fun request(url: URI): Request { return Request( HttpRequest.newBuilder(url) .GET() .header("user-agent", "Firmament/${Firmament.version}") ) } }