Skip to content

Commit

Permalink
Merge pull request #713 from hexagontk/develop
Browse files Browse the repository at this point in the history
Update dependencies and tests
  • Loading branch information
jaguililla committed Aug 13, 2024
2 parents 45e6c3c + b823d1f commit 27d5b48
Show file tree
Hide file tree
Showing 33 changed files with 870 additions and 259 deletions.
39 changes: 39 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,42 @@ jobs:
uses: hexagontk/.github/.github/workflows/graalvm_gradle.yml@master
with:
check_directory: core/build

# Jobs for debugging purposes, activate commenting 'if'
os_build:
strategy:
fail-fast: false
matrix:
os: [ windows-latest, macos-latest ]

if: false
name: os_build (${{ matrix.os }})
uses: hexagontk/.github/.github/workflows/graalvm_gradle.yml@master
with:
os: ${{ matrix.os }}
check_directory: core/build

native_test:
strategy:
fail-fast: false
matrix:
os: [ windows-latest, macos-latest ]

if: false
name: native_test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
steps:
- uses: al-cheb/[email protected]
if: ${{ matrix.os == 'windows-latest' }}
with:
minimum-size: 16GB
disk-root: "C:"
- uses: actions/checkout@v4
with:
ref: develop
- uses: graalvm/setup-graalvm@v1
with:
java-version: 21
distribution: graalvm-community
cache: gradle
- run: ./gradlew --stacktrace nativeTest
4 changes: 3 additions & 1 deletion .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ permissions:
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- uses: actions/stale@v9
with:
Expand Down Expand Up @@ -62,7 +64,7 @@ jobs:
with:
minimum-size: 16GB
disk-root: "C:"
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
ref: develop
- uses: graalvm/setup-graalvm@v1
Expand Down
8 changes: 8 additions & 0 deletions core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,11 @@ Cryptography and key stores utilities.

# Package com.hexagonkt.core.text
Text utilities to allow the use of ANSI escape codes and case converting tools among other features.

> TODO Create 'Pairs' class to model a list of pairs with repeatable keys
> Should allow to convert to list if keys are null, grouping values if there are duplicates
> Use it in serialization and HTTP fields (query params, forms, etc.)
>
> TODO Remove logger class and make log utilities extension methods (creating a loggerOf() helper)
>
> TODO logger and security may be removed and their content moved to root
41 changes: 41 additions & 0 deletions core/src/main/kotlin/com/hexagonkt/core/Uuids.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,47 @@ import com.hexagonkt.core.text.encodeToBase64
import java.nio.ByteBuffer
import java.util.*

// TODO UUIDv7: https://antonz.org/uuidv7/?ref=dailydev#kotlin
/*
import java.security.SecureRandom
import java.time.Instant
object UUIDv7 {
private val random = SecureRandom()
fun generate(): ByteArray {
// random bytes
val value = ByteArray(16)
random.nextBytes(value)
// current timestamp in ms
val timestamp = Instant.now().toEpochMilli()
// timestamp
value[0] = ((timestamp shr 40) and 0xFF).toByte()
value[1] = ((timestamp shr 32) and 0xFF).toByte()
value[2] = ((timestamp shr 24) and 0xFF).toByte()
value[3] = ((timestamp shr 16) and 0xFF).toByte()
value[4] = ((timestamp shr 8) and 0xFF).toByte()
value[5] = (timestamp and 0xFF).toByte()
// version and variant
value[6] = (value[6].toInt() and 0x0F or 0x70).toByte()
value[8] = (value[8].toInt() and 0x3F or 0x80).toByte()
return value
}
@JvmStatic
fun main(args: Array<String>) {
val uuidVal = generate()
uuidVal.forEach { b -> print("%02x".format(b)) }
println()
}
}
*/
// TODO Rename to Ids.kt and support other algorithms (nanoid, ulid, snowflake, cuid)

/**
* [TODO](https://github.com/hexagontk/hexagon/issues/271).
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,13 @@ package com.hexagonkt.core.security

import com.hexagonkt.core.urlOf
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.condition.DisabledOnOs
import org.junit.jupiter.api.condition.OS.WINDOWS
import java.io.File
import java.security.cert.X509Certificate
import kotlin.test.assertEquals

internal class KeyStoresTest {

@Test
@DisabledOnOs(WINDOWS) // TODO Fails in windows because Algorithm HmacPBESHA256 not available
fun `Key stores are loaded correctly`() {
@Test fun `Key stores are loaded correctly`() {
val n = "hexagontk"
val f = "$n.p12"
val pwd = f.reversed()
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ org.gradle.warning.mode=all
org.gradle.console=plain

# Gradle
version=3.6.1
version=3.6.2
group=com.hexagonkt
description=The atoms of your platform

Expand Down
2 changes: 2 additions & 0 deletions gradle/certificates.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -160,5 +160,7 @@ private static String createSan(

final List<String> allSubdomains = testSubdomains + [ "${domain}.test", "localhost" ]
final List<String> fullSubdomains = allSubdomains.collect { "dns:${it}".toString() }
// TODO Useful if IP should be added, refactor to support scenario
// fullSubdomains.add("ip:127.0.0.1")
return fullSubdomains.join(",")
}
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,7 @@ class HttpClient(
private fun HttpHandler.process(
request: HttpRequestPort, attributes: Map<String, Any>
): HttpContext =
HttpContext(HttpCall(request = request), handlerPredicate, attributes = attributes)
.let { context ->
if (handlerPredicate(context)) process(context) as HttpContext
else context
}
processHttp(
HttpContext(HttpCall(request = request), handlerPredicate, attributes = attributes)
)
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.hexagonkt.http.client

import com.hexagonkt.core.media.TEXT_CSV
import com.hexagonkt.core.media.TEXT_PLAIN
import com.hexagonkt.core.urlOf
import com.hexagonkt.http.handlers.FilterHandler
import com.hexagonkt.http.handlers.HttpPredicate
import com.hexagonkt.http.model.HttpResponsePort
import com.hexagonkt.http.model.*
import com.hexagonkt.http.patterns.LiteralPathPattern
import java.lang.StringBuilder
import java.util.concurrent.Flow
import java.util.concurrent.Flow.Subscription
Expand Down Expand Up @@ -139,6 +142,15 @@ internal class HttpClientTest {
assertEquals("HTTP client *MUST BE STARTED* before shut-down", message)
}

@Test fun `HTTP clients fails to start if already started`() {
val client = HttpClient(VoidAdapter)
client.start()
assert(client.started())
val message = assertFailsWith<IllegalStateException> { client.start() }.message
assertEquals("HTTP client is already started", message)
client.stop()
}

@Test fun `Handlers filter requests and responses`() {
val handler = FilterHandler {
val next = receive(body = "p_" + request.bodyString()).next()
Expand All @@ -155,6 +167,38 @@ internal class HttpClientTest {
}
}

@Test fun `Request is sent even if no handler`() {
val pathPattern = LiteralPathPattern("/test")
val handler = FilterHandler(HttpPredicate(pathPattern = pathPattern)) { error("Failure") }
val client = HttpClient(VoidAdapter, handler = handler)

val e1 = assertFailsWith<IllegalStateException> { client.get("http://localhost") }
assertEquals("HTTP client *MUST BE STARTED* before sending requests", e1.message)

client.start()
client.request {
val e2 = assertFailsWith<IllegalStateException> { client.put("/test", "body") }
assertEquals("Failure", e2.message)
assertEquals("/good", client.put("/good", "body").headers["-path-"]?.string())
}
}

@Test fun `Request is sent with accept and authorization headers`() {
val handler = FilterHandler {
assertEquals(TEXT_PLAIN, request.accept.first().mediaType)
assertEquals("basic", request.authorization?.type)
assertEquals("abc", request.authorization?.value)
next()
}
val client = HttpClient(VoidAdapter, handler = handler)

client.request {
val accept = listOf(ContentType(TEXT_PLAIN))
val authorization = Authorization("basic", "abc")
client.send(HttpRequest(accept = accept, authorization = authorization))
}
}

@Test fun `SSE requests work properly`() {
val client = HttpClient(VoidAdapter)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,14 @@ class JavaClientAdapter : HttpClientPort {
.newBuilder(URI(uri))
.method(request.method.toString(), BodyPublishers.ofByteArray(bodyBytes))

request.headers.forEach { e ->
val name = e.value.name
if (name != "accept-encoding") // TODO Maybe accept-encoding interferes with H2C
e.value.values.forEach { h -> javaRequest.setHeader(name, h.toString()) }
request.headers.forEach { h ->
val name = h.value.name
val values = h.value.values
// TODO Maybe accept-encoding interferes with H2C
if (name != "accept-encoding" && values.isNotEmpty()) {
val kvs = values.flatMap { v -> listOf(name, v.toString()) }.toTypedArray()
javaRequest.headers(*kvs)
}
}

request.contentType?.let { javaRequest.setHeader("content-type", it.text) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,23 @@ import com.hexagonkt.http.test.examples.*
import com.hexagonkt.serialization.jackson.JacksonTextFormat
import com.hexagonkt.serialization.jackson.json.Json
import com.hexagonkt.serialization.jackson.yaml.Yaml
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.condition.DisabledOnOs
import org.junit.jupiter.api.condition.OS.WINDOWS

val clientAdapter: () -> JavaClientAdapter = ::JavaClientAdapter
val serverAdapter: () -> JettyServletAdapter = ::JettyServletAdapter
val formats: List<JacksonTextFormat> = listOf(Json, Yaml)

// TODO Add multipart and file upload tests
internal class AdapterBooksTest : BooksTest(clientAdapter, serverAdapter)
internal class AdapterErrorsTest : ErrorsTest(clientAdapter, serverAdapter)
internal class AdapterFiltersTest : FiltersTest(clientAdapter, serverAdapter)
internal class AdapterClientTest : ClientTest(clientAdapter, serverAdapter, formats) {
// TODO
@Test @Disabled override fun `Parameters are set properly`() {}
// TODO
@Test @Disabled override fun `Form parameters are sent correctly`() {}
}
@DisabledOnOs(WINDOWS) // TODO Make this work on GitHub runners
internal class AdapterClientTest : ClientTest(clientAdapter, serverAdapter, formats)
internal class AdapterClientCookiesTest : ClientCookiesTest(clientAdapter, serverAdapter, formats)
internal class AdapterClientHttp2Test : ClientHttp2Test(clientAdapter, serverAdapter, formats)
internal class AdapterClientHttpsTest : ClientHttpsTest(clientAdapter, serverAdapter, formats)
internal class AdapterHttpsTest : HttpsTest(clientAdapter, serverAdapter)
internal class AdapterZipTest : ZipTest(clientAdapter, serverAdapter)
internal class AdapterCookiesTest : CookiesTest(clientAdapter, serverAdapter)
@Disabled // TODO
internal class AdapterFilesTest : FilesTest(clientAdapter, serverAdapter)
internal class AdapterCorsTest : CorsTest(clientAdapter, serverAdapter)
internal class AdapterSamplesTest : SamplesTest(clientAdapter, serverAdapter) {
// TODO
@Test @Disabled override fun callbacks() {}
}
internal class AdapterSamplesTest : SamplesTest(clientAdapter, serverAdapter)
internal class AdapterBenchmarkIT : BenchmarkIT(clientAdapter, serverAdapter)
2 changes: 1 addition & 1 deletion http/http_handlers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ HTML Form processing. Don't parse body!
## File Uploads
Multipart Requests

@code http/http_test/src/main/kotlin/com/hexagonkt/http/test/examples/SamplesTest.kt?callbackFile
@code http/http_test/src/main/kotlin/com/hexagonkt/http/test/examples/MultipartSamplesTest.kt?callbackFile

## Response
Response information is provided by the `response` field:
Expand Down
9 changes: 9 additions & 0 deletions http/http_handlers/api/http_handlers.api
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public final class com/hexagonkt/http/handlers/AfterHandler : com/hexagonkt/hand
public fun hashCode ()I
public fun process (Lcom/hexagonkt/handlers/Context;)Lcom/hexagonkt/handlers/Context;
public fun process (Lcom/hexagonkt/http/model/HttpRequestPort;)Lcom/hexagonkt/http/handlers/HttpContext;
public fun processHttp (Lcom/hexagonkt/http/handlers/HttpContext;)Lcom/hexagonkt/http/handlers/HttpContext;
public fun toString ()Ljava/lang/String;
}

Expand All @@ -47,6 +48,7 @@ public final class com/hexagonkt/http/handlers/BeforeHandler : com/hexagonkt/han
public fun hashCode ()I
public fun process (Lcom/hexagonkt/handlers/Context;)Lcom/hexagonkt/handlers/Context;
public fun process (Lcom/hexagonkt/http/model/HttpRequestPort;)Lcom/hexagonkt/http/handlers/HttpContext;
public fun processHttp (Lcom/hexagonkt/http/handlers/HttpContext;)Lcom/hexagonkt/http/handlers/HttpContext;
public fun toString ()Ljava/lang/String;
}

Expand All @@ -71,6 +73,7 @@ public final class com/hexagonkt/http/handlers/ExceptionHandler : com/hexagonkt/
public fun hashCode ()I
public fun process (Lcom/hexagonkt/handlers/Context;)Lcom/hexagonkt/handlers/Context;
public fun process (Lcom/hexagonkt/http/model/HttpRequestPort;)Lcom/hexagonkt/http/handlers/HttpContext;
public fun processHttp (Lcom/hexagonkt/http/handlers/HttpContext;)Lcom/hexagonkt/http/handlers/HttpContext;
public fun toString ()Ljava/lang/String;
}

Expand All @@ -97,6 +100,7 @@ public final class com/hexagonkt/http/handlers/FilterHandler : com/hexagonkt/han
public fun hashCode ()I
public fun process (Lcom/hexagonkt/handlers/Context;)Lcom/hexagonkt/handlers/Context;
public fun process (Lcom/hexagonkt/http/model/HttpRequestPort;)Lcom/hexagonkt/http/handlers/HttpContext;
public fun processHttp (Lcom/hexagonkt/http/handlers/HttpContext;)Lcom/hexagonkt/http/handlers/HttpContext;
public fun toString ()Ljava/lang/String;
}

Expand Down Expand Up @@ -300,6 +304,7 @@ public final class com/hexagonkt/http/handlers/HttpController$DefaultImpls {
public static fun getPredicate (Lcom/hexagonkt/http/handlers/HttpController;)Lkotlin/jvm/functions/Function1;
public static fun process (Lcom/hexagonkt/http/handlers/HttpController;Lcom/hexagonkt/handlers/Context;)Lcom/hexagonkt/handlers/Context;
public static fun process (Lcom/hexagonkt/http/handlers/HttpController;Lcom/hexagonkt/http/model/HttpRequestPort;)Lcom/hexagonkt/http/handlers/HttpContext;
public static fun processHttp (Lcom/hexagonkt/http/handlers/HttpController;Lcom/hexagonkt/http/handlers/HttpContext;)Lcom/hexagonkt/http/handlers/HttpContext;
}

public abstract interface class com/hexagonkt/http/handlers/HttpExceptionCallback : kotlin/jvm/functions/Function2 {
Expand All @@ -311,12 +316,14 @@ public abstract interface class com/hexagonkt/http/handlers/HttpHandler : com/he
public abstract fun filter (Lcom/hexagonkt/http/model/HttpMethod;)Lcom/hexagonkt/http/handlers/HttpHandler;
public abstract fun getHandlerPredicate ()Lcom/hexagonkt/http/handlers/HttpPredicate;
public abstract fun process (Lcom/hexagonkt/http/model/HttpRequestPort;)Lcom/hexagonkt/http/handlers/HttpContext;
public abstract fun processHttp (Lcom/hexagonkt/http/handlers/HttpContext;)Lcom/hexagonkt/http/handlers/HttpContext;
}

public final class com/hexagonkt/http/handlers/HttpHandler$DefaultImpls {
public static fun byMethod (Lcom/hexagonkt/http/handlers/HttpHandler;)Ljava/util/Map;
public static fun filter (Lcom/hexagonkt/http/handlers/HttpHandler;Lcom/hexagonkt/http/model/HttpMethod;)Lcom/hexagonkt/http/handlers/HttpHandler;
public static fun process (Lcom/hexagonkt/http/handlers/HttpHandler;Lcom/hexagonkt/http/model/HttpRequestPort;)Lcom/hexagonkt/http/handlers/HttpContext;
public static fun processHttp (Lcom/hexagonkt/http/handlers/HttpHandler;Lcom/hexagonkt/http/handlers/HttpContext;)Lcom/hexagonkt/http/handlers/HttpContext;
}

public final class com/hexagonkt/http/handlers/HttpPredicate : kotlin/jvm/functions/Function1 {
Expand Down Expand Up @@ -369,6 +376,7 @@ public final class com/hexagonkt/http/handlers/OnHandler : com/hexagonkt/handler
public fun hashCode ()I
public fun process (Lcom/hexagonkt/handlers/Context;)Lcom/hexagonkt/handlers/Context;
public fun process (Lcom/hexagonkt/http/model/HttpRequestPort;)Lcom/hexagonkt/http/handlers/HttpContext;
public fun processHttp (Lcom/hexagonkt/http/handlers/HttpContext;)Lcom/hexagonkt/http/handlers/HttpContext;
public fun toString ()Ljava/lang/String;
}

Expand All @@ -393,6 +401,7 @@ public final class com/hexagonkt/http/handlers/PathHandler : com/hexagonkt/handl
public fun hashCode ()I
public fun process (Lcom/hexagonkt/handlers/Context;)Lcom/hexagonkt/handlers/Context;
public fun process (Lcom/hexagonkt/http/model/HttpRequestPort;)Lcom/hexagonkt/http/handlers/HttpContext;
public fun processHttp (Lcom/hexagonkt/http/handlers/HttpContext;)Lcom/hexagonkt/http/handlers/HttpContext;
public fun toString ()Ljava/lang/String;
}

Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ sealed interface HttpHandler : Handler<HttpCall> {
this
}

fun processHttp(context: HttpContext): HttpContext =
if (handlerPredicate(context)) process(context) as HttpContext
else context

fun process(request: HttpRequestPort): HttpContext =
HttpContext(HttpCall(request = request), handlerPredicate).let { context ->
if (handlerPredicate(context)) process(context) as HttpContext
else context
}
processHttp(HttpContext(HttpCall(request = request), handlerPredicate))
}
Loading

0 comments on commit 27d5b48

Please sign in to comment.