Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Require JVM 17+ and cookie improvements #660

Merged
merged 8 commits into from
Sep 3, 2023
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
3 changes: 1 addition & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ plugins {
id("idea")
id("eclipse")
id("project-report")
id("org.jetbrains.dokka") version("1.8.20")
id("org.jetbrains.dokka") version("1.9.0")
id("com.github.jk1.dependency-license-report") version("2.5")
id("org.jetbrains.kotlinx.binary-compatibility-validator") version("0.13.2")
id("org.graalvm.buildtools.native") version("0.9.25") apply(false)
Expand Down Expand Up @@ -153,7 +153,6 @@ apiValidation {
// Experimental modules
"rest",
"rest_tools",
// "serverless",
"web",
)
)
Expand Down
8 changes: 4 additions & 4 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ org.gradle.warning.mode=all
org.gradle.console=plain

# Gradle
version=3.0.5
version=3.1.0
group=com.hexagonkt
description=The atoms of your platform

Expand All @@ -32,12 +32,12 @@ iconsDirectory=content

# VERSIONS
kotlinVersion=1.9.10
dokkaVersion=1.8.20
dokkaVersion=1.9.0
mockkVersion=1.13.7
junitVersion=5.10.0
gatlingVersion=3.9.5
jmhVersion=1.37
mkdocsMaterialVersion=9.2.6
mkdocsMaterialVersion=9.2.7
mermaidDokkaVersion=0.4.4
nativeToolsVersion=0.9.25

Expand All @@ -50,7 +50,7 @@ nimaVersion=4.0.0-M1

# http_server_servlet
servletVersion=6.0.0
#jettyVersion=12.0.1 Failing with HTTP client gzip encoding
#jettyVersion=12.0.1 TODO Failing with HTTP client gzip encoding
jettyVersion=12.0.0

# rest_tools
Expand Down
2 changes: 1 addition & 1 deletion gradle/kotlin.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ apply(plugin: "maven-publish")
defaultTasks("build")

java {
sourceCompatibility = JavaVersion.toVersion(findProperty("jvmTarget") ?: "11")
sourceCompatibility = JavaVersion.toVersion(findProperty("jvmTarget") ?: "17")
targetCompatibility = sourceCompatibility
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ data class ChainHandler<T : Any>(
constructor(vararg handlers: Handler<T>) : this(handlers.toList(), { true })

override fun process(context: Context<T>): Context<T> {
val event = context.event
val nestedContext = context.with(event = event, nextHandlers = handlers, nextHandler = 0)
val nestedContext = context.with(nextHandlers = handlers, nextHandler = 0)
val nestedResult = nestedContext.next()
val followUpContext = nestedResult.with(
predicate = predicate,
Expand Down
21 changes: 15 additions & 6 deletions http/http/api/http.api
Original file line number Diff line number Diff line change
Expand Up @@ -70,19 +70,19 @@ public final class com/hexagonkt/http/model/ContentType {
}

public final class com/hexagonkt/http/model/Cookie {
public fun <init> (Ljava/lang/String;Ljava/lang/String;JZLjava/lang/String;ZLjava/lang/String;ZLjava/time/Instant;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;JZLjava/lang/String;ZLjava/lang/String;ZLjava/time/Instant;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;JZLjava/lang/String;ZLjava/lang/String;Lcom/hexagonkt/http/model/CookieSameSite;Ljava/time/Instant;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;JZLjava/lang/String;ZLjava/lang/String;Lcom/hexagonkt/http/model/CookieSameSite;Ljava/time/Instant;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/lang/String;
public final fun component3 ()J
public final fun component4 ()Z
public final fun component5 ()Ljava/lang/String;
public final fun component6 ()Z
public final fun component7 ()Ljava/lang/String;
public final fun component8 ()Z
public final fun component8 ()Lcom/hexagonkt/http/model/CookieSameSite;
public final fun component9 ()Ljava/time/Instant;
public final fun copy (Ljava/lang/String;Ljava/lang/String;JZLjava/lang/String;ZLjava/lang/String;ZLjava/time/Instant;)Lcom/hexagonkt/http/model/Cookie;
public static synthetic fun copy$default (Lcom/hexagonkt/http/model/Cookie;Ljava/lang/String;Ljava/lang/String;JZLjava/lang/String;ZLjava/lang/String;ZLjava/time/Instant;ILjava/lang/Object;)Lcom/hexagonkt/http/model/Cookie;
public final fun copy (Ljava/lang/String;Ljava/lang/String;JZLjava/lang/String;ZLjava/lang/String;Lcom/hexagonkt/http/model/CookieSameSite;Ljava/time/Instant;)Lcom/hexagonkt/http/model/Cookie;
public static synthetic fun copy$default (Lcom/hexagonkt/http/model/Cookie;Ljava/lang/String;Ljava/lang/String;JZLjava/lang/String;ZLjava/lang/String;Lcom/hexagonkt/http/model/CookieSameSite;Ljava/time/Instant;ILjava/lang/Object;)Lcom/hexagonkt/http/model/Cookie;
public final fun delete ()Lcom/hexagonkt/http/model/Cookie;
public fun equals (Ljava/lang/Object;)Z
public final fun getDeleted ()Z
Expand All @@ -92,13 +92,22 @@ public final class com/hexagonkt/http/model/Cookie {
public final fun getMaxAge ()J
public final fun getName ()Ljava/lang/String;
public final fun getPath ()Ljava/lang/String;
public final fun getSameSite ()Z
public final fun getSameSite ()Lcom/hexagonkt/http/model/CookieSameSite;
public final fun getSecure ()Z
public final fun getValue ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class com/hexagonkt/http/model/CookieSameSite : java/lang/Enum {
public static final field LAX Lcom/hexagonkt/http/model/CookieSameSite;
public static final field NONE Lcom/hexagonkt/http/model/CookieSameSite;
public static final field STRICT Lcom/hexagonkt/http/model/CookieSameSite;
public static fun getEntries ()Lkotlin/enums/EnumEntries;
public static fun valueOf (Ljava/lang/String;)Lcom/hexagonkt/http/model/CookieSameSite;
public static fun values ()[Lcom/hexagonkt/http/model/CookieSameSite;
}

public final class com/hexagonkt/http/model/FormParameter : com/hexagonkt/http/model/HttpField {
public fun <init> (Ljava/lang/String;Ljava/util/List;)V
public fun <init> (Ljava/lang/String;[Ljava/lang/Object;)V
Expand Down
19 changes: 16 additions & 3 deletions http/http/src/main/kotlin/com/hexagonkt/http/model/Cookie.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,28 @@ package com.hexagonkt.http.model

import java.time.Instant

/**
* TODO .
*
* @property name
* @property value
* @property maxAge '-1' is the same as empty
* @property secure
* @property path '/' is the same as empty
* @property httpOnly
* @property domain
* @property sameSite
* @property expires
*/
data class Cookie(
val name: String,
val value: String = "",
val maxAge: Long = -1,
val secure: Boolean = false,
val path: String = "/",
val httpOnly: Boolean = true,
val domain: String = "",
val sameSite: Boolean = true,
val httpOnly: Boolean = false,
val domain: String? = null,
val sameSite: CookieSameSite? = null,
val expires: Instant? = null,
) {
val deleted: Boolean by lazy { value == "" && maxAge <= 0L }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.hexagonkt.http.model

enum class CookieSameSite {
STRICT,
LAX,
NONE,
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.hexagonkt.http.client.HttpClientPort
import com.hexagonkt.http.client.HttpClientSettings
import com.hexagonkt.http.model.HttpResponse
import com.hexagonkt.http.model.*
import com.hexagonkt.http.model.CookieSameSite.*
import com.hexagonkt.http.model.ws.WsSession
import com.hexagonkt.http.parseContentType
import org.eclipse.jetty.client.HttpResponseException
Expand All @@ -21,6 +22,7 @@ import org.eclipse.jetty.client.BytesRequestContent
import org.eclipse.jetty.client.MultiPartRequestContent
import org.eclipse.jetty.client.StringRequestContent
import org.eclipse.jetty.http.HttpCookie
import org.eclipse.jetty.http.HttpCookie.SameSite
import org.eclipse.jetty.http.HttpCookieStore
import org.eclipse.jetty.http.HttpFields
import org.eclipse.jetty.http.HttpFields.EMPTY
Expand Down Expand Up @@ -142,7 +144,17 @@ open class JettyClientAdapter : HttpClientPort {

if (settings.useCookies)
adapterHttpClient.cookies = adapterJettyClient.httpCookieStore.all().map {
Cookie(it.name, it.value, it.maxAge, it.isSecure)
Cookie(
it.name,
it.value,
it.maxAge,
it.isSecure,
it.path,
it.isHttpOnly,
it.domain,
it.attributes["SameSite"]?.uppercase()?.let(CookieSameSite::valueOf),
expires = it.expires,
)
}

return HttpResponse(
Expand Down Expand Up @@ -243,7 +255,21 @@ open class JettyClientAdapter : HttpClientPort {
val httpCookie = java.net.HttpCookie(it.name, it.value)
httpCookie.secure = it.secure
httpCookie.maxAge = it.maxAge
store.add(uri, HttpCookie.from(httpCookie))
httpCookie.path = it.path
httpCookie.isHttpOnly = it.httpOnly
it.domain?.let(httpCookie::setDomain)

val from = HttpCookie.build(httpCookie).expires(it.expires)

it.sameSite?.let { ss ->
when(ss){
STRICT -> SameSite.STRICT
LAX -> SameSite.LAX
NONE -> SameSite.NONE
}
}?.let { ss -> from.sameSite(ss) }

store.add(uri, from.build())
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ import io.netty.handler.codec.http.cookie.Cookie
import io.netty.handler.codec.http.cookie.ServerCookieDecoder
import io.netty.handler.codec.http.multipart.*
import io.netty.handler.codec.http.HttpRequest
import io.netty.handler.codec.http.cookie.CookieHeaderNames.SameSite.*
import io.netty.handler.codec.http.cookie.DefaultCookie
import java.net.InetSocketAddress
import java.net.URI
import java.security.cert.X509Certificate
import kotlin.Long.Companion.MIN_VALUE
import io.netty.handler.codec.http.HttpMethod as NettyHttpMethod

class NettyRequestAdapter(
Expand Down Expand Up @@ -93,8 +96,18 @@ class NettyRequestAdapter(
Cookie(
name = it.name(),
value = it.value(),
maxAge = if (it.maxAge() == Long.MIN_VALUE) -1 else it.maxAge(),
maxAge = if (it.maxAge() == MIN_VALUE) -1 else it.maxAge(),
secure = it.isSecure,
path = it.path() ?: "/",
httpOnly = it.isHttpOnly,
sameSite = (it as? DefaultCookie)?.sameSite()?.let { ss ->
when (ss) {
Strict -> CookieSameSite.STRICT
Lax -> CookieSameSite.LAX
None -> CookieSameSite.NONE
}
},
domain = it.domain(),
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.hexagonkt.http.model.*
import com.hexagonkt.http.model.Cookie
import com.hexagonkt.http.handlers.HttpHandler
import com.hexagonkt.http.handlers.HttpContext
import com.hexagonkt.http.model.CookieSameSite.*
import com.hexagonkt.http.model.HttpCall
import com.hexagonkt.http.model.HttpResponse
import io.netty.buffer.Unpooled
Expand All @@ -22,8 +23,9 @@ import io.netty.handler.codec.http.HttpMethod.GET
import io.netty.handler.codec.http.HttpRequest
import io.netty.handler.codec.http.HttpResponseStatus.*
import io.netty.handler.codec.http.HttpVersion.HTTP_1_1
import io.netty.handler.codec.http.cookie.CookieHeaderNames.SameSite.*
import io.netty.handler.codec.http.cookie.DefaultCookie
import io.netty.handler.codec.http.cookie.ServerCookieEncoder.STRICT
import io.netty.handler.codec.http.cookie.ServerCookieEncoder.STRICT as STRICT_ENCODER
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory
import io.netty.handler.ssl.SslHandler
import io.netty.handler.ssl.SslHandshakeCompletionEvent
Expand Down Expand Up @@ -94,7 +96,7 @@ internal class NettyServerHandler(

val hexagonCookies = response.cookies
if (hexagonCookies.isNotEmpty())
headers[SET_COOKIE] = STRICT.encode(nettyCookies(hexagonCookies))
headers[SET_COOKIE] = STRICT_ENCODER.encode(nettyCookies(hexagonCookies))

val contentType = response.contentType
if (contentType != null)
Expand Down Expand Up @@ -189,7 +191,7 @@ internal class NettyServerHandler(

val hexagonCookies = hexagonResponse.cookies
if (hexagonCookies.isNotEmpty())
headers[SET_COOKIE] = STRICT.encode(nettyCookies(hexagonCookies))
headers[SET_COOKIE] = STRICT_ENCODER.encode(nettyCookies(hexagonCookies))

val contentType = hexagonResponse.contentType
if (contentType != null)
Expand All @@ -211,6 +213,19 @@ internal class NettyServerHandler(
if (it.maxAge != -1L)
setMaxAge(it.maxAge)
isSecure = it.secure
setPath(it.path)
setDomain(it.domain)
isHttpOnly = it.httpOnly
it.domain?.let(::setDomain)
it.sameSite
?.let { ss ->
when (ss) {
STRICT -> Strict
LAX -> Lax
NONE -> None
}
}
?.let(::setSameSite)
}
}

Expand Down
2 changes: 1 addition & 1 deletion http/http_server_nima/api/http_server_nima.api
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public final class com/hexagonkt/http/server/nima/NimaRequestAdapter : com/hexag
}

public final class com/hexagonkt/http/server/nima/NimaServerAdapter : com/hexagonkt/http/server/HttpServerPort {
public static final field startErrorMessage Ljava/lang/String;
public static final field START_ERROR_MESSAGE Ljava/lang/String;
public fun <init> ()V
public fun options ()Ljava/util/Map;
public fun runtimePort ()I
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ import javax.net.ssl.TrustManagerFactory
class NimaServerAdapter : HttpServerPort {

private companion object {
const val startErrorMessage = "Nima server not started correctly"
const val START_ERROR_MESSAGE = "Nima server not started correctly"
}

private var nimaServer: WebServer? = null

override fun runtimePort(): Int {
return nimaServer?.port() ?: error(startErrorMessage)
return nimaServer?.port() ?: error(START_ERROR_MESSAGE)
}

override fun started() =
Expand Down Expand Up @@ -76,11 +76,11 @@ class NimaServerAdapter : HttpServerPort {

nimaServer = serverBuilder.build()

nimaServer?.start() ?: error(startErrorMessage)
nimaServer?.start() ?: error(START_ERROR_MESSAGE)
}

override fun shutDown() {
nimaServer?.stop() ?: error(startErrorMessage)
nimaServer?.stop() ?: error(START_ERROR_MESSAGE)
}

override fun supportedProtocols(): Set<HttpProtocol> =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ class ServletFilter(pathHandler: HttpHandler) : HttpFilter() {
val cookie = Cookie(it.name, it.value).apply {
maxAge = it.maxAge.toInt()
secure = it.secure
path = it.path
isHttpOnly = it.httpOnly
it.domain?.let { d -> domain = d }
}
servletResponse.addCookie(cookie)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,17 @@ internal abstract class ServletRequestAdapter(req: HttpServletRequest) : HttpReq

override val cookies: List<Cookie> by lazy {
req.cookies
?.map { Cookie(it.name, it.value, it.maxAge.toLong(), it.secure) }
?.map {
Cookie(
it.name,
it.value,
it.maxAge.toLong(),
it.secure,
it.path ?: "/",
it.isHttpOnly,
it.domain,
)
}
?: emptyList()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ abstract class ClientTest(
assertNull(cookiesMap["c3"]) // Secure headers only sent through HTTPS
ok(cookies = listOf(
Cookie("c4", "v4", 60),
Cookie("c5", "v5", secure = false),
Cookie("c5", "v5"),
Cookie("c6", "v6", secure = true),
))
}
Expand All @@ -154,13 +154,13 @@ abstract class ClientTest(
val responseC4 = response.cookiesMap().require("c4")
assertEquals("v4", responseC4.value)
assertTrue(responseC4.maxAge in 59..60)
assertEquals(Cookie("c5", "v5", secure = false), response.cookiesMap()["c5"])
assertEquals(Cookie("c5", "v5", domain = "localhost"), response.cookiesMap()["c5"])
assertNull(response.cookiesMap()["c6"])

val clientC4 = client.cookiesMap().require("c4")
assertEquals("v4", clientC4.value)
assertTrue(clientC4.maxAge in 59..60)
assertEquals(Cookie("c5", "v5", secure = false), client.cookiesMap()["c5"])
assertEquals(Cookie("c5", "v5", domain = "localhost"), client.cookiesMap()["c5"])
assertNull(client.cookiesMap()["c6"])
}

Expand Down
Loading