Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions components/proxy/src/test/kotlin/com/hotels/styx/Support.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright (C) 2013-2023 Expedia Inc.
Copyright (C) 2013-2026 Expedia Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -46,11 +46,14 @@ import io.mockk.CapturingSlot
import io.mockk.every
import io.mockk.mockk
import org.slf4j.LoggerFactory
import reactor.core.publisher.Mono
import reactor.kotlin.core.publisher.toMono
import java.nio.charset.StandardCharsets.UTF_8
import java.util.concurrent.CompletableFuture
import java.util.concurrent.Executor

fun <T : Any> Mono<T>.blockRequired(): T = block() ?: error("Expected a value but Mono was empty")

fun routingObjectDef(text: String) = YamlConfig(text).`as`(StyxObjectDefinition::class.java)

fun configBlock(text: String) = YamlConfig(text).`as`(JsonNode::class.java)
Expand Down Expand Up @@ -177,29 +180,29 @@ fun mockObjectFactory(objects: List<RoutingObject>) = mockk<RoutingObjectFactory

private val LOGGER = LoggerFactory.getLogger("ProxySupport")

fun CompletableFuture<HttpResponse>.wait(debug: Boolean = false) = this.toMono()
fun CompletableFuture<HttpResponse>.wait(debug: Boolean = false): HttpResponse = this.toMono()
.doOnNext {
if (debug) {
LOGGER.debug("${it.status()} - ${it.headers()} - ${it.bodyAs(UTF_8)}")
}
}
.block()
.blockRequired()

fun CompletableFuture<LiveHttpResponse>.wait(debug: Boolean = false) = this.toMono()
fun CompletableFuture<LiveHttpResponse>.wait(debug: Boolean = false): LiveHttpResponse = this.toMono()
.doOnNext {
if (debug) {
LOGGER.debug("${it.status()} - ${it.headers()}")
}
}
.block()
.blockRequired()

fun Eventual<LiveHttpResponse>.wait(maxBytes: Int = 100 * 1024, debug: Boolean = false) = this.toMono()
fun Eventual<LiveHttpResponse>.wait(maxBytes: Int = 100 * 1024, debug: Boolean = false): HttpResponse = this.toMono()
.flatMap { it.aggregate(maxBytes).toMono() }
.doOnNext {
if (debug) {
LOGGER.info("${it.status()} - ${it.headers()} - ${it.bodyAs(UTF_8)}")
}
}
.block()
.blockRequired()

fun requestContext(secure: Boolean = false, executor: Executor = Executor { it.run() }) = HttpInterceptorContext(secure, null, executor)
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright (C) 2013-2023 Expedia Inc.
Copyright (C) 2013-2026 Expedia Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -23,6 +23,7 @@ import com.hotels.styx.api.HttpRequest.put
import com.hotels.styx.api.HttpResponseStatus.CREATED
import com.hotels.styx.api.HttpResponseStatus.NOT_FOUND
import com.hotels.styx.api.HttpResponseStatus.OK
import com.hotels.styx.blockRequired
import com.hotels.styx.handle
import com.hotels.styx.mockObject
import com.hotels.styx.routing.RoutingMetadataDecorator
Expand Down Expand Up @@ -59,7 +60,7 @@ class RoutingObjectHandlerTest : FeatureSpec({
.body(staticResponseObject, UTF_8)
.build())
.toMono()
.block()!!
.blockRequired()
.status() shouldBe CREATED

routeDatabase.get("staticResponse").isPresent shouldBe true
Expand All @@ -76,14 +77,14 @@ class RoutingObjectHandlerTest : FeatureSpec({
.body(staticResponseObject, UTF_8)
.build())
.toMono()
.block()!!
.blockRequired()
.status() shouldBe CREATED

handler.handle(get("/admin/routing/objects/staticResponse").build())
.toMono()
.block()
.blockRequired()
.let {
it!!.status() shouldBe OK
it.status() shouldBe OK
it.bodyAs(UTF_8).trim() shouldBe """
---
type: "StaticResponseHandler"
Expand All @@ -106,7 +107,7 @@ class RoutingObjectHandlerTest : FeatureSpec({
.body(staticResponseObject, UTF_8)
.build())
.toMono()
.block()!!
.blockRequired()
.status() shouldBe CREATED

handler.handle(
Expand All @@ -121,7 +122,7 @@ class RoutingObjectHandlerTest : FeatureSpec({
""".trimIndent(), UTF_8)
.build())
.toMono()
.block()
.blockRequired()
.let {
it.status() shouldBe CREATED
}
Expand All @@ -130,9 +131,9 @@ class RoutingObjectHandlerTest : FeatureSpec({

handler.handle(get("/admin/routing/objects").build())
.toMono()
.block()
.blockRequired()
.let {
it!!.status() shouldBe OK
it.status() shouldBe OK
it.bodyAs(UTF_8) shouldContain """
conditionRouter:
type: "ConditionRouter"
Expand Down Expand Up @@ -175,7 +176,7 @@ class RoutingObjectHandlerTest : FeatureSpec({
""".trimIndent(), UTF_8)
.build())
.toMono()
.block()!!
.blockRequired()
.status() shouldBe CREATED

db.get("staticResponse").isPresent shouldBe true
Expand All @@ -195,7 +196,7 @@ class RoutingObjectHandlerTest : FeatureSpec({
handler.handle(
delete("/admin/routing/objects/staticResponse").build())
.toMono()
.block()!!
.blockRequired()
.status() shouldBe OK

db.get("staticResponse").isPresent shouldBe false
Expand All @@ -211,7 +212,7 @@ class RoutingObjectHandlerTest : FeatureSpec({
handler.handle(
delete("/admin/routing/objects/staticResponse").build())
.toMono()
.block()!!
.blockRequired()
.status() shouldBe NOT_FOUND
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright (C) 2013-2025 Expedia Inc.
Copyright (C) 2013-2026 Expedia Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright (C) 2013-2023 Expedia Inc.
Copyright (C) 2013-2026 Expedia Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -20,8 +20,10 @@ import com.hotels.styx.api.HttpHandler
import com.hotels.styx.api.HttpHeaders
import com.hotels.styx.api.HttpRequest
import com.hotels.styx.api.HttpRequest.get
import com.hotels.styx.api.HttpResponse
import com.hotels.styx.api.configuration.ObjectStore
import com.hotels.styx.api.exceptions.NoAvailableHostsException
import com.hotels.styx.blockRequired
import com.hotels.styx.handle
import com.hotels.styx.lbGroupTag
import com.hotels.styx.requestContext
Expand Down Expand Up @@ -179,7 +181,7 @@ class LoadBalancingGroupTest : FeatureSpec() {
routeDb.get("appx-B").get().routingObject.metric().ongoingActivities() shouldBe 20

invocations.forEach {
val response = it.block()
val response = it.blockRequired()
LOGGER.debug("response: ${response.bodyAs(UTF_8)}")
}
}
Expand Down Expand Up @@ -215,7 +217,7 @@ internal fun Publisher<ObjectStore<RoutingObjectRecord>>.waitUntil(duration: Dur
.blockFirst(duration)


internal fun HttpHandler.call(request: HttpRequest, maxContentBytes: Int = 100000) = this.handle(request.stream(), requestContext())
internal fun HttpHandler.call(request: HttpRequest, maxContentBytes: Int = 100000): HttpResponse = this.handle(request.stream(), requestContext())
.flatMap { it.aggregate(maxContentBytes) }
.toMono()
.block()
.blockRequired()
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright (C) 2013-2023 Expedia Inc.
Copyright (C) 2013-2026 Expedia Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -15,6 +15,7 @@
*/
package com.hotels.styx.servers

import com.hotels.styx.blockRequired
import com.hotels.styx.InetServer
import com.hotels.styx.RoutingObjectFactoryContext
import com.hotels.styx.StyxObjectRecord
Expand Down Expand Up @@ -73,7 +74,7 @@ class StyxHttpServerTest : FeatureSpec({
.header(HOST, "localhost:${server.inetAddress()!!.port}")
.build())
.wait()
?.let {
.let {
it.status() shouldBe OK
it.bodyAs(UTF_8) shouldBe "Hello, test!"
}
Expand Down Expand Up @@ -104,7 +105,7 @@ class StyxHttpServerTest : FeatureSpec({
.header(HOST, "localhost:${server.inetAddress()!!.port}")
.build())
.wait()
?.let {
.let {
it.status() shouldBe OK
it.bodyAs(UTF_8) shouldBe "Hello, test!"
}
Expand All @@ -130,7 +131,7 @@ class StyxHttpServerTest : FeatureSpec({
.header(HOST, "localhost:${server.inetAddress()!!.port}")
.header("accept-encoding", "7z, gzip")
.build())
.wait()!!
.wait()
.let {
it.status() shouldBe OK
it.header("content-encoding").get() shouldBe "gzip"
Expand All @@ -142,7 +143,7 @@ class StyxHttpServerTest : FeatureSpec({
StyxHttpClient.Builder().build().send(get("/blah")
.header(HOST, "localhost:${server.inetAddress()!!.port}")
.build())
.wait()!!
.wait()
.let {
it.status() shouldBe OK
it.header("content-encoding").isPresent shouldBe false
Expand All @@ -168,7 +169,7 @@ class StyxHttpServerTest : FeatureSpec({
StyxHttpClient.Builder().build().send(get("/a/" + "b".repeat(80))
.header(HOST, "localhost:${server.inetAddress()!!.port}")
.build())
.wait()!!
.wait()
.let {
it.status() shouldBe OK
it.bodyAs(UTF_8) shouldBe "Hello, test!"
Expand All @@ -178,8 +179,8 @@ class StyxHttpServerTest : FeatureSpec({
scenario("Rejects requests exceeding the initial line length") {
StyxHttpClient.Builder().build().send(get("/a/" + "b".repeat(95))
.header(HOST, "localhost:${server.inetAddress()!!.port}")
.build())!!
.wait()!!
.build())
.wait()
.let {
it.status() shouldBe REQUEST_ENTITY_TOO_LARGE
it.bodyAs(UTF_8) shouldBe "Request Entity Too Large"
Expand All @@ -205,7 +206,7 @@ class StyxHttpServerTest : FeatureSpec({
StyxHttpClient.Builder().build().send(get("/a/" + "b".repeat(80))
.header(HOST, "localhost:${server.inetAddress()!!.port}")
.build())
.wait()!!
.wait()
.let {
it.status() shouldBe OK
it.bodyAs(UTF_8) shouldBe "Hello, test!"
Expand All @@ -217,8 +218,8 @@ class StyxHttpServerTest : FeatureSpec({
.header(HOST, "localhost:${server.inetAddress()!!.port}")
.header("test-1", "x".repeat(1024))
.header("test-2", "x".repeat(1024))
.build())!!
.wait()!!
.build())
.wait()
.let {
it.status() shouldBe REQUEST_ENTITY_TOO_LARGE
it.bodyAs(UTF_8) shouldBe "Request Entity Too Large"
Expand Down Expand Up @@ -252,8 +253,8 @@ class StyxHttpServerTest : FeatureSpec({
.build())
.wait()
.aggregate(1024)
.toFlux()
.blockFirst()!!
.toMono()
.blockRequired()
.let {
it.status() shouldBe REQUEST_TIMEOUT
it.header(CONNECTION).get() shouldBe "close"
Expand Down Expand Up @@ -281,7 +282,7 @@ class StyxHttpServerTest : FeatureSpec({
.createConnection(
newOriginBuilder("localhost", server.inetAddress()!!.port).build(),
ConnectionSettings(250))
.block()!!
.blockRequired()

scenario("Should keep HTTP1/1 client connection open after serving the response.") {
connection.write(
Expand All @@ -290,10 +291,10 @@ class StyxHttpServerTest : FeatureSpec({
.build()
.stream(), DummyContext)
.toMono()
.block()!!
.blockRequired()
.aggregate(1024)
.toMono()
.block()!!
.blockRequired()

Thread.sleep(100)

Expand Down Expand Up @@ -348,8 +349,8 @@ class StyxHttpServerTest : FeatureSpec({

StyxHttpClient.Builder().build().send(get("/a/" + "b".repeat(95))
.header(HOST, "localhost:${server.inetAddress()!!.port}")
.build())!!
.wait()!!
.build())
.wait()
.let {
it.status() shouldBe OK
}
Expand All @@ -374,7 +375,7 @@ fun threadNames() = Thread.getAllStackTraces().keys
private fun createConnection(port: Int) = NettyConnectionFactory.Builder()
.build()
.createConnection(newOriginBuilder("localhost", port).build(), ConnectionSettings(250))
.block()!!
.blockRequired()

private val response = response(OK)
.header("source", "secure")
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
<okhttp.version>4.12.0</okhttp.version>
<pcollections.version>5.0.0</pcollections.version>
<reactive-streams.version>1.0.4</reactive-streams.version>
<reactor.version>2024.0.17</reactor.version>
<reactor.version>2025.0.5</reactor.version>

<!--Kotlin -->
<kotlin.version>2.3.21</kotlin.version>
Expand Down
Loading