All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hexagonkt.http.test.examples.ClientHttp2Test.kt Maven / Gradle / Ivy

package com.hexagonkt.http.test.examples

import com.hexagonkt.core.require
import com.hexagonkt.core.media.APPLICATION_JSON
import com.hexagonkt.core.urlOf
import com.hexagonkt.http.SslSettings
import com.hexagonkt.http.client.HttpClient
import com.hexagonkt.http.client.HttpClientPort
import com.hexagonkt.http.client.HttpClientSettings
import com.hexagonkt.http.formatQueryString
import com.hexagonkt.http.model.*
import com.hexagonkt.http.server.*
import com.hexagonkt.http.handlers.HttpCallbackType
import com.hexagonkt.http.handlers.HttpHandler
import com.hexagonkt.http.handlers.path
import com.hexagonkt.http.model.HttpProtocol.HTTP2
import com.hexagonkt.http.test.BaseTest
import com.hexagonkt.serialization.SerializationFormat
import com.hexagonkt.serialization.SerializationManager
import org.junit.jupiter.api.*
import org.junit.jupiter.api.condition.DisabledIf
import java.net.URL

import kotlin.test.assertEquals

@Suppress("FunctionName") // This class's functions are intended to be used only in tests
abstract class ClientHttp2Test(
    final override val clientAdapter: () -> HttpClientPort,
    final override val serverAdapter: () -> HttpServerPort,
    private val serializationFormats: List,
    final override val serverSettings: HttpServerSettings = HttpServerSettings(),
) : BaseTest() {

    private var callback: HttpCallbackType = { this }

    override val handler: HttpHandler = path {
        post("*") { callback() }
        get("*") { callback() }
        head("*") { callback() }
        put("*") { callback() }
        delete("*") { callback() }
        trace("*") { callback() }
        options("*") { callback() }
        patch("*") { callback() }
    }

    @BeforeAll fun setUpSerializationFormats() {
        SerializationManager.formats = serializationFormats.toSet()
    }

    @BeforeEach fun resetHandler() {
        callback = {
            val contentType = ContentType(APPLICATION_JSON, charset = Charsets.UTF_8)
            val bodyString = request.bodyString()
            val bodyHeader =
                if (bodyString.endsWith("\n") || bodyString.contains("{")) "json"
                else bodyString

            ok(
                body = bodyString,
                headers = response.headers
                    + Header("body", bodyHeader)
                    + Header("ct", request.contentType?.text ?: "")
                    + Header("query-parameters", formatQueryString(queryParameters)),
                contentType = contentType,
            )
        }
    }

    @Test
    @DisabledIf("nativeMac")
    fun `Request HTTPS example`() {

        val serverAdapter = serverAdapter()

        // Key store files
        val identity = "hexagontk.p12"
        val trust = "trust.p12"

        // Default passwords are file name reversed
        val keyStorePassword = identity.reversed()
        val trustStorePassword = trust.reversed()

        // Key stores can be set as URIs to classpath resources (the triple slash is needed)
        val keyStore = urlOf("classpath:ssl/$identity")
        val trustStore = urlOf("classpath:ssl/$trust")

        val sslSettings = SslSettings(
            keyStore = keyStore,
            keyStorePassword = keyStorePassword,
            trustStore = trustStore,
            trustStorePassword = trustStorePassword,
            clientAuth = true // Requires a valid certificate from the client (mutual TLS)
        )

        val serverSettings = serverSettings.copy(
            bindPort = 0,
            protocol = HTTP2,
            sslSettings = sslSettings
        )

        val server = serve(serverAdapter, serverSettings) {
            get("/hello") {
                // We can access the certificate used by the client from the request
                val subjectDn = request.certificate()?.subjectX500Principal?.name ?: ""
                ok("Hello World!", headers = response.headers + Header("cert", subjectDn) )
            }
        }

        // We'll use the same certificate for the client (in a real scenario it would be different)
        val baseUrl = serverBase(server)
        val clientSettings = HttpClientSettings(baseUrl = baseUrl, sslSettings = sslSettings)

        // Create an HTTP client and make an HTTPS request
        val client = HttpClient(clientAdapter(), clientSettings)
        client.start()
        client.get("/hello").apply {
            // Assure the certificate received (and returned) by the server is correct
            assert(headers.require("cert").string()?.startsWith("CN=hexagontk.com") ?: false)
            assertEquals(body, "Hello World!")
        }

        client.stop()
        server.stop()
    }

    private fun serverBase(server: HttpServer): URL =
        urlOf("${server.binding.protocol}://localhost:${server.runtimePort}")

    @Suppress("MemberVisibilityCanBePrivate") // Public access required by JUnit
    fun nativeMac(): Boolean =
        System.getProperty("os.name").lowercase().contains("mac")
            && System.getProperty("org.graalvm.nativeimage.imagecode") != null
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy