Skip to content

Commit

Permalink
Merge pull request #715 from hexagontk/develop
Browse files Browse the repository at this point in the history
Minor improvements
  • Loading branch information
jaguililla authored Aug 16, 2024
2 parents 27d5b48 + 152263d commit 9fcad79
Show file tree
Hide file tree
Showing 40 changed files with 223 additions and 76 deletions.
2 changes: 1 addition & 1 deletion core/src/main/kotlin/com/hexagonkt/core/Jvm.kt
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ object Jvm {
* found on the OS environment variables or in JVM system properties.
*/
fun <T: Any> systemSettingOrNull(type: KClass<T>, name: String): T? =
systemSettingRaw(name).let { it.parseOrNull(type) }
systemSettingRaw(name).parseOrNull(type)

fun <T: Any> systemSetting(type: KClass<T>, name: String): T =
systemSettingOrNull(type, name)
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.2
version=3.6.3
group=com.hexagonkt
description=The atoms of your platform

Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
5 changes: 4 additions & 1 deletion gradlew
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#

##############################################################################
#
Expand Down Expand Up @@ -84,7 +86,8 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
Expand Down
2 changes: 2 additions & 0 deletions gradlew.bat
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem

@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
Expand Down
3 changes: 3 additions & 0 deletions http/http/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ TODO

# Package com.hexagonkt.http.patterns
TODO

> TODO Capabilities enum for client/servers to declare its functionalities (along with protocols)
> enum class HttpCapability { MULTIPART, COOKIES, SSE, WEBSOCKET }
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ class HttpClient(
path("*", listOf(it, sendHandler))
}

private val noRequestSettings =
settings.contentType == null
&& settings.authorization == null
&& settings.accept.isEmpty()
&& settings.headers.isEmpty()

var cookies: List<Cookie> = emptyList()

override fun close() {
Expand Down Expand Up @@ -65,12 +71,15 @@ class HttpClient(
?: adapter.send(request.setUp())

private fun HttpRequest.setUp(): HttpRequest {
return copy(
contentType = contentType ?: settings.contentType,
accept = accept.ifEmpty(settings::accept),
headers = settings.headers + headers,
authorization = authorization ?: settings.authorization,
)
return if (noRequestSettings)
this
else
copy(
contentType = contentType ?: settings.contentType,
accept = accept.ifEmpty(settings::accept),
headers = settings.headers + headers,
authorization = authorization ?: settings.authorization,
)
}

fun sse(request: HttpRequest): Publisher<ServerEvent> =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import com.hexagonkt.http.SslSettings
import com.hexagonkt.http.model.*
import java.net.URL

// TODO Add proxy configuration
// TODO Add proxy configuration and timeouts
data class HttpClientSettings(
val baseUrl: URL? = null,
val contentType: ContentType? = null,
Expand Down
2 changes: 2 additions & 0 deletions http/http_client_java/api/http_client_java.api
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
public final class com/hexagonkt/http/client/java/JavaClientAdapter : com/hexagonkt/http/client/HttpClientPort {
public fun <init> ()V
public fun <init> (Lcom/hexagonkt/http/model/HttpProtocol;Ljava/util/concurrent/Executor;)V
public synthetic fun <init> (Lcom/hexagonkt/http/model/HttpProtocol;Ljava/util/concurrent/Executor;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun send (Lcom/hexagonkt/http/model/HttpRequestPort;)Lcom/hexagonkt/http/model/HttpResponsePort;
public fun shutDown ()V
public fun sse (Lcom/hexagonkt/http/model/HttpRequestPort;)Ljava/util/concurrent/Flow$Publisher;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import com.hexagonkt.http.client.HttpClientSettings
import com.hexagonkt.http.formatQueryString
import com.hexagonkt.http.handlers.bodyToBytes
import com.hexagonkt.http.model.*
import com.hexagonkt.http.model.HttpProtocol.H2C
import com.hexagonkt.http.model.HttpProtocol.HTTP2
import com.hexagonkt.http.model.HttpResponse
import com.hexagonkt.http.model.ws.WsSession
import com.hexagonkt.http.parseContentType
Expand All @@ -18,12 +20,14 @@ import java.net.HttpCookie
import java.net.URI
import java.net.http.HttpClient.Redirect.ALWAYS
import java.net.http.HttpClient.Redirect.NEVER
import java.net.http.HttpClient.Version.HTTP_1_1
import java.net.http.HttpClient.Version.HTTP_2
import java.net.http.HttpHeaders
import java.net.http.HttpRequest.BodyPublishers
import java.net.http.HttpResponse.BodyHandlers
import java.security.SecureRandom
import java.security.cert.X509Certificate
import java.util.concurrent.Executor
import java.util.concurrent.Flow.Publisher
import javax.net.ssl.KeyManagerFactory
import javax.net.ssl.SSLContext
Expand All @@ -36,7 +40,10 @@ import java.net.http.HttpResponse as JavaHttpResponse
/**
* Client to use other REST services.
*/
class JavaClientAdapter : HttpClientPort {
class JavaClientAdapter(
private val protocol: HttpProtocol = HTTP2,
private val executor: Executor? = null,
) : HttpClientPort {

private companion object {
object TrustAll : X509TrustManager {
Expand All @@ -62,12 +69,15 @@ class JavaClientAdapter : HttpClientPort {
httpSettings = settings
val javaClientBuilder = JavaHttpClient
.newBuilder()
.version(HTTP_2)
.version(if (protocol == HTTP2 || protocol == H2C) HTTP_2 else HTTP_1_1)
.followRedirects(if (settings.followRedirects) ALWAYS else NEVER)

if (settings.useCookies)
javaClientBuilder.cookieHandler(cookieManager)

if (executor != null)
javaClientBuilder.executor(executor)

settings.sslSettings?.let { javaClientBuilder.sslContext(sslContext(it)) }

javaClient = javaClientBuilder.build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import com.hexagonkt.handlers.Handler
import com.hexagonkt.http.model.*
import com.hexagonkt.http.model.HttpCall

sealed interface HttpHandler : Handler<HttpCall> {
interface HttpHandler : Handler<HttpCall> {
val handlerPredicate: HttpPredicate

fun addPrefix(prefix: String): HttpHandler
Expand Down
18 changes: 18 additions & 0 deletions http/http_server/api/http_server.api
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,21 @@ public final class com/hexagonkt/http/server/callbacks/UrlCallback : kotlin/jvm/
public synthetic fun invoke (Ljava/lang/Object;)Ljava/lang/Object;
}

public final class com/hexagonkt/http/server/handlers/CorsHandler : com/hexagonkt/http/handlers/HttpHandler {
public fun <init> ()V
public fun <init> (Lcom/hexagonkt/http/server/callbacks/CorsCallback;)V
public fun <init> (Ljava/lang/String;Lcom/hexagonkt/http/server/callbacks/CorsCallback;)V
public synthetic fun <init> (Ljava/lang/String;Lcom/hexagonkt/http/server/callbacks/CorsCallback;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Lkotlin/text/Regex;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;ZLcom/hexagonkt/http/model/HttpStatus;J)V
public synthetic fun <init> (Ljava/lang/String;Lkotlin/text/Regex;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;ZLcom/hexagonkt/http/model/HttpStatus;JILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun addPrefix (Ljava/lang/String;)Lcom/hexagonkt/http/handlers/HttpHandler;
public fun byMethod ()Ljava/util/Map;
public fun filter (Lcom/hexagonkt/http/model/HttpMethod;)Lcom/hexagonkt/http/handlers/HttpHandler;
public fun getCallback ()Lkotlin/jvm/functions/Function1;
public fun getHandlerPredicate ()Lcom/hexagonkt/http/handlers/HttpPredicate;
public fun getPredicate ()Lkotlin/jvm/functions/Function1;
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;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.hexagonkt.http.server.handlers

import com.hexagonkt.http.handlers.FilterHandler
import com.hexagonkt.http.handlers.HttpHandler
import com.hexagonkt.http.handlers.HttpPredicate
import com.hexagonkt.http.model.HttpMethod
import com.hexagonkt.http.model.HttpMethod.Companion.ALL
import com.hexagonkt.http.model.HttpStatus
import com.hexagonkt.http.model.NO_CONTENT_204
import com.hexagonkt.http.server.callbacks.CorsCallback

// TODO Write tests
class CorsHandler(pattern: String = "*", cors: CorsCallback = CorsCallback()) :
HttpHandler by FilterHandler(HttpPredicate(pattern = pattern), cors) {

constructor(cors: CorsCallback) : this("*", cors)

constructor(
pattern: String = "*",
allowedOrigin: Regex,
allowedMethods: Set<HttpMethod> = ALL,
allowedHeaders: Set<String> = emptySet(),
exposedHeaders: Set<String> = emptySet(),
supportCredentials: Boolean = true,
preFlightStatus: HttpStatus = NO_CONTENT_204,
preFlightMaxAge: Long = 0
) : this(
pattern,
CorsCallback(
allowedOrigin,
allowedMethods,
allowedHeaders,
exposedHeaders,
supportCredentials,
preFlightStatus,
preFlightMaxAge,
)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ internal object VoidAdapter : HttpServerPort {
override fun started() = started
override fun startUp(server: HttpServer) { started = true }
override fun shutDown() { started = false }
override fun supportedProtocols(): Set<HttpProtocol> = HttpProtocol.values().toSet() - H2C
override fun supportedProtocols(): Set<HttpProtocol> = HttpProtocol.entries.toSet() - H2C
override fun supportedFeatures(): Set<HttpServerFeature> = setOf(SSE)
override fun options(): Map<String, *> = mapOf("option1" to 1, "option2" to 2)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import com.hexagonkt.http.patterns.TemplatePathPattern
import com.hexagonkt.http.handlers.HttpContext
import com.hexagonkt.http.handlers.HttpPredicate
import org.junit.jupiter.api.Test
import java.io.File
import kotlin.IllegalStateException
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
Expand Down
2 changes: 2 additions & 0 deletions http/http_server_helidon/api/http_server_helidon.api
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public final class com/hexagonkt/http/server/helidon/HelidonRequestAdapter : com
public final class com/hexagonkt/http/server/helidon/HelidonServerAdapter : com/hexagonkt/http/server/HttpServerPort {
public static final field START_ERROR_MESSAGE Ljava/lang/String;
public fun <init> ()V
public fun <init> (IILjava/time/Duration;Ljava/time/Duration;ZZZZZZ)V
public synthetic fun <init> (IILjava/time/Duration;Ljava/time/Duration;ZZZZZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun options ()Ljava/util/Map;
public fun runtimePort ()I
public fun shutDown ()V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ import com.hexagonkt.http.server.HttpServer
import com.hexagonkt.http.server.HttpServerFeature
import com.hexagonkt.http.server.HttpServerFeature.ZIP
import com.hexagonkt.http.server.HttpServerPort
import io.helidon.common.socket.SocketOptions
import io.helidon.http.Method
import io.helidon.http.Status
import io.helidon.http.HeaderNames
import io.helidon.http.HttpMediaType
import io.helidon.http.SetCookie
import io.helidon.webserver.WebServer
import io.helidon.webserver.http.ServerResponse
import io.helidon.webserver.http1.Http1Config
import io.helidon.webserver.http2.Http2Config
import java.security.SecureRandom
import java.time.Duration
import javax.net.ssl.KeyManagerFactory
Expand All @@ -31,8 +34,21 @@ import javax.net.ssl.TrustManagerFactory

/**
* Implements [HttpServerPort] using Helidon.
*
* TODO Add settings for HTTP2 and separate them on constructor parameters
*/
class HelidonServerAdapter : HttpServerPort {
class HelidonServerAdapter(
private val backlog: Int = 1_024,
private val writeQueueLength: Int = 0,
private val readTimeout: Duration = Duration.ofSeconds(30),
private val connectTimeout: Duration = Duration.ofSeconds(10),
private val tcpNoDelay: Boolean = false,
private val receiveLog: Boolean = true,
private val sendLog: Boolean = true,
private val validatePath: Boolean = true,
private val validateRequestHeaders: Boolean = true,
private val validateResponseHeaders: Boolean = false,
) : HttpServerPort {

private companion object {
const val START_ERROR_MESSAGE = "Helidon server not started correctly"
Expand All @@ -56,6 +72,7 @@ class HelidonServerAdapter : HttpServerPort {
.byMethod()
.mapKeys { Method.create(it.key.toString()) }

// TODO features(): [Config, Encoding, Media, WebServer] Maybe Multipart can be added there
val serverBuilder = WebServer
.builder()
.host(settings.bindAddress.hostName)
Expand All @@ -77,7 +94,34 @@ class HelidonServerAdapter : HttpServerPort {
.sslContext(sslContext(sslSettings))
}

helidonServer = serverBuilder.build()
val protocolConfig =
if (settings.protocol == HTTP || settings.protocol == HTTPS)
Http1Config
.builder()
.receiveLog(receiveLog)
.sendLog(sendLog)
.validatePath(validatePath)
.validateRequestHeaders(validateRequestHeaders)
.validateResponseHeaders(validateResponseHeaders)
.build()
else
Http2Config
.builder()
.validatePath(validatePath)
.build()

helidonServer = serverBuilder
.backlog(backlog)
.writeQueueLength(writeQueueLength)
.connectionOptions(SocketOptions
.builder()
.readTimeout(readTimeout)
.connectTimeout(connectTimeout)
.tcpNoDelay(tcpNoDelay)
.build()
)
.protocols(listOf(protocolConfig))
.build()

helidonServer?.start() ?: error(START_ERROR_MESSAGE)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ open class NettyServerAdapter(
}

override fun supportedProtocols(): Set<HttpProtocol> =
setOf(HTTP, HTTPS, HTTP2, H2C)
setOf(HTTP, HTTPS, HTTP2)

override fun supportedFeatures(): Set<HttpServerFeature> =
setOf(ZIP, WEB_SOCKETS, SSE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,16 @@ internal class PeriodicPublisher<T>(
supplier: Supplier<out T>,
) : SubmissionPublisher<T>(executor, maxBufferCapacity) {

private val scheduler: ScheduledExecutorService
private val periodicTask: ScheduledFuture<*>

init {
scheduler = ScheduledThreadPoolExecutor(1)
periodicTask = scheduler.scheduleAtFixedRate(
{
val get = supplier.get()
submit(get)
},
0,
period,
unit,
)
}
private val scheduler: ScheduledExecutorService = ScheduledThreadPoolExecutor(1)
private val periodicTask: ScheduledFuture<*> = scheduler.scheduleAtFixedRate(
{
val get = supplier.get()
submit(get)
},
0,
period,
unit,
)

override fun close() {
periodicTask.cancel(false)
Expand Down
Loading

0 comments on commit 9fcad79

Please sign in to comment.