Skip to content

Commit 38a1d37

Browse files
authored
Enable JEP-380 when supported (#3151)
* Enable JEP-380 when supported supported when: - running Java 16+ - using file based unix domain socket paths (non-abstract) also deprecated `unix_domain_socket` in favor of the generalized `unix_domain_sockets` to allow more multiple ingress. * Add new generated artifacts for changes to WebConfig * opt to not include deprecated annotation * Update tests to ignore if not running Java 16+.
1 parent 880f387 commit 38a1d37

File tree

9 files changed

+1618
-20
lines changed

9 files changed

+1618
-20
lines changed

.palantir/revapi.yml

+1,502
Large diffs are not rendered by default.

gradle/libs.versions.toml

+1
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ jettyServer = { module = "org.eclipse.jetty:jetty-server" }
102102
jettyServlet = { module = "org.eclipse.jetty:jetty-servlet" }
103103
jettyServletApi = { module = "org.eclipse.jetty.toolchain:jetty-servlet-api", version = "4.0.6" }
104104
jettyServlets = { module = "org.eclipse.jetty:jetty-servlets" }
105+
jettyUds = { module = "org.eclipse.jetty:jetty-unixdomain-server" }
105106
jettyUnixSocket = { module = "org.eclipse.jetty:jetty-unixsocket-server" }
106107
jettyUtil = { module = "org.eclipse.jetty:jetty-util" }
107108
jettyWebsocketApi = { module = "org.eclipse.jetty.websocket:websocket-jetty-api" }

misk/api/misk.api

+6-3
Original file line numberDiff line numberDiff line change
@@ -1451,7 +1451,8 @@ public final class misk/web/WebConfig : wisp/config/Config {
14511451
public fun <init> (IJILjava/lang/String;Lmisk/web/WebSslConfig;Lmisk/web/WebUnixDomainSocketConfig;ZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;IIIZLmisk/web/exceptions/ActionExceptionLogLevelConfig;Ljava/lang/Integer;DZILjava/util/Map;ZLorg/slf4j/event/Level;Lmisk/web/ConcurrencyLimiterConfig;ILjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Long;IIZZLjava/lang/Integer;Ljava/lang/Integer;)V
14521452
public fun <init> (IJILjava/lang/String;Lmisk/web/WebSslConfig;Lmisk/web/WebUnixDomainSocketConfig;ZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;IIIZLmisk/web/exceptions/ActionExceptionLogLevelConfig;Ljava/lang/Integer;DZILjava/util/Map;ZLorg/slf4j/event/Level;Lmisk/web/ConcurrencyLimiterConfig;ILjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Long;IIZZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;)V
14531453
public fun <init> (IJILjava/lang/String;Lmisk/web/WebSslConfig;Lmisk/web/WebUnixDomainSocketConfig;ZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;IIIZLmisk/web/exceptions/ActionExceptionLogLevelConfig;Ljava/lang/Integer;DZILjava/util/Map;ZLorg/slf4j/event/Level;Lmisk/web/ConcurrencyLimiterConfig;ILjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Long;IIZZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Z)V
1454-
public synthetic fun <init> (IJILjava/lang/String;Lmisk/web/WebSslConfig;Lmisk/web/WebUnixDomainSocketConfig;ZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;IIIZLmisk/web/exceptions/ActionExceptionLogLevelConfig;Ljava/lang/Integer;DZILjava/util/Map;ZLorg/slf4j/event/Level;Lmisk/web/ConcurrencyLimiterConfig;ILjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Long;IIZZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;ZIILkotlin/jvm/internal/DefaultConstructorMarker;)V
1454+
public fun <init> (IJILjava/lang/String;Lmisk/web/WebSslConfig;Lmisk/web/WebUnixDomainSocketConfig;ZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;IIIZLmisk/web/exceptions/ActionExceptionLogLevelConfig;Ljava/lang/Integer;DZILjava/util/Map;ZLorg/slf4j/event/Level;Lmisk/web/ConcurrencyLimiterConfig;ILjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Long;IIZZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Z[Lmisk/web/WebUnixDomainSocketConfig;)V
1455+
public synthetic fun <init> (IJILjava/lang/String;Lmisk/web/WebSslConfig;Lmisk/web/WebUnixDomainSocketConfig;ZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;IIIZLmisk/web/exceptions/ActionExceptionLogLevelConfig;Ljava/lang/Integer;DZILjava/util/Map;ZLorg/slf4j/event/Level;Lmisk/web/ConcurrencyLimiterConfig;ILjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Long;IIZZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Z[Lmisk/web/WebUnixDomainSocketConfig;IILkotlin/jvm/internal/DefaultConstructorMarker;)V
14551456
public final fun component1 ()I
14561457
public final fun component10 ()Ljava/lang/Integer;
14571458
public final fun component11 ()I
@@ -1481,14 +1482,15 @@ public final class misk/web/WebConfig : wisp/config/Config {
14811482
public final fun component33 ()Ljava/lang/Integer;
14821483
public final fun component34 ()Ljava/lang/Integer;
14831484
public final fun component35 ()Z
1485+
public final fun component36 ()[Lmisk/web/WebUnixDomainSocketConfig;
14841486
public final fun component4 ()Ljava/lang/String;
14851487
public final fun component5 ()Lmisk/web/WebSslConfig;
14861488
public final fun component6 ()Lmisk/web/WebUnixDomainSocketConfig;
14871489
public final fun component7 ()Z
14881490
public final fun component8 ()Ljava/lang/Integer;
14891491
public final fun component9 ()Ljava/lang/Integer;
1490-
public final fun copy (IJILjava/lang/String;Lmisk/web/WebSslConfig;Lmisk/web/WebUnixDomainSocketConfig;ZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;IIIZLmisk/web/exceptions/ActionExceptionLogLevelConfig;Ljava/lang/Integer;DZILjava/util/Map;ZLorg/slf4j/event/Level;Lmisk/web/ConcurrencyLimiterConfig;ILjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Long;IIZZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Z)Lmisk/web/WebConfig;
1491-
public static synthetic fun copy$default (Lmisk/web/WebConfig;IJILjava/lang/String;Lmisk/web/WebSslConfig;Lmisk/web/WebUnixDomainSocketConfig;ZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;IIIZLmisk/web/exceptions/ActionExceptionLogLevelConfig;Ljava/lang/Integer;DZILjava/util/Map;ZLorg/slf4j/event/Level;Lmisk/web/ConcurrencyLimiterConfig;ILjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Long;IIZZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;ZIILjava/lang/Object;)Lmisk/web/WebConfig;
1492+
public final fun copy (IJILjava/lang/String;Lmisk/web/WebSslConfig;Lmisk/web/WebUnixDomainSocketConfig;ZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;IIIZLmisk/web/exceptions/ActionExceptionLogLevelConfig;Ljava/lang/Integer;DZILjava/util/Map;ZLorg/slf4j/event/Level;Lmisk/web/ConcurrencyLimiterConfig;ILjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Long;IIZZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Z[Lmisk/web/WebUnixDomainSocketConfig;)Lmisk/web/WebConfig;
1493+
public static synthetic fun copy$default (Lmisk/web/WebConfig;IJILjava/lang/String;Lmisk/web/WebSslConfig;Lmisk/web/WebUnixDomainSocketConfig;ZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;IIIZLmisk/web/exceptions/ActionExceptionLogLevelConfig;Ljava/lang/Integer;DZILjava/util/Map;ZLorg/slf4j/event/Level;Lmisk/web/ConcurrencyLimiterConfig;ILjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Long;IIZZLjava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Z[Lmisk/web/WebUnixDomainSocketConfig;IILjava/lang/Object;)Lmisk/web/WebConfig;
14921494
public fun equals (Ljava/lang/Object;)Z
14931495
public final fun getAcceptors ()Ljava/lang/Integer;
14941496
public final fun getAction_exception_log_level ()Lmisk/web/exceptions/ActionExceptionLogLevelConfig;
@@ -1524,6 +1526,7 @@ public final class misk/web/WebConfig : wisp/config/Config {
15241526
public final fun getShutdown_sleep_ms ()I
15251527
public final fun getSsl ()Lmisk/web/WebSslConfig;
15261528
public final fun getUnix_domain_socket ()Lmisk/web/WebUnixDomainSocketConfig;
1529+
public final fun getUnix_domain_sockets ()[Lmisk/web/WebUnixDomainSocketConfig;
15271530
public final fun getUse_virtual_threads ()Z
15281531
public fun hashCode ()I
15291532
public fun toString ()Ljava/lang/String;

misk/build.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ dependencies {
4545
implementation(libs.jettyIo)
4646
implementation(libs.jettyServlet)
4747
implementation(libs.jettyServlets)
48+
implementation(libs.jettyUds)
4849
implementation(libs.jettyUnixSocket)
4950
implementation(libs.jettyWebsocketApi)
5051
implementation(libs.jettyWebsocketServer)

misk/src/main/kotlin/misk/web/WebConfig.kt

+4-1
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,9 @@ data class WebConfig @JvmOverloads constructor(
150150

151151
/** Wires up health checks on whether Jetty's thread pool is low on threads. */
152152
val enable_thread_pool_health_check: Boolean = false,
153+
154+
/** Configurations to enable Jetty to listen for traffic on a unix domain socket being proxied through a sidecar (e.g. envoy, istio) */
155+
val unix_domain_sockets: Array<WebUnixDomainSocketConfig>? = null,
153156
) : Config
154157

155158
data class WebSslConfig @JvmOverloads constructor(
@@ -181,7 +184,7 @@ data class WebSslConfig @JvmOverloads constructor(
181184
}
182185

183186
data class WebUnixDomainSocketConfig @JvmOverloads constructor(
184-
/** The Unix Domain Socket to listen on. */
187+
/** The Unix Domain Socket to listen on. Will attempt to use the JEP-380 connector when supported (using Java 16+ and file path) */
185188
val path: String,
186189
/** If true, the listener will support H2C. */
187190
val h2c: Boolean? = true

misk/src/main/kotlin/misk/web/jetty/JettyService.kt

+57-13
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
package misk.web.jetty
22

33
import com.google.common.base.Stopwatch
4+
import com.google.common.base.Strings
45
import com.google.common.util.concurrent.AbstractIdleService
56
import com.google.common.util.concurrent.ThreadFactoryBuilder
7+
import com.squareup.wire.internal.newMutableList
68
import jakarta.inject.Inject
79
import jakarta.inject.Singleton
810
import misk.security.ssl.CipherSuites
911
import misk.security.ssl.SslLoader
1012
import misk.security.ssl.TlsProtocols
1113
import misk.web.WebConfig
1214
import misk.web.WebSslConfig
15+
import misk.web.WebUnixDomainSocketConfig
1316
import misk.web.mediatype.MediaTypes
1417
import okhttp3.HttpUrl
1518
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory
@@ -33,18 +36,22 @@ import org.eclipse.jetty.servlet.FilterHolder
3336
import org.eclipse.jetty.servlet.ServletContextHandler
3437
import org.eclipse.jetty.servlet.ServletHolder
3538
import org.eclipse.jetty.servlets.CrossOriginFilter
39+
import org.eclipse.jetty.unixdomain.server.UnixDomainServerConnector
3640
import org.eclipse.jetty.unixsocket.server.UnixSocketConnector
41+
import org.eclipse.jetty.util.JavaVersion
3742
import org.eclipse.jetty.util.ssl.SslContextFactory
3843
import org.eclipse.jetty.util.thread.ThreadPool
3944
import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer
4045
import wisp.logging.getLogger
46+
import java.io.File
4147
import java.lang.Thread.sleep
4248
import java.net.InetAddress
4349
import java.nio.file.InvalidPathException
4450
import java.util.EnumSet
4551
import java.util.concurrent.SynchronousQueue
4652
import java.util.concurrent.ThreadPoolExecutor
4753
import java.util.concurrent.TimeUnit
54+
import java.util.regex.Pattern
4855
import javax.servlet.DispatcherType
4956

5057
@Singleton
@@ -230,25 +237,49 @@ class JettyService @Inject internal constructor(
230237
server.addConnector(httpsConnector)
231238
}
232239

240+
var socketConfigs = newMutableList<WebUnixDomainSocketConfig>()
233241
if (webConfig.unix_domain_socket != null) {
242+
socketConfigs.add(webConfig.unix_domain_socket)
243+
}
244+
if (webConfig.unix_domain_sockets != null) {
245+
socketConfigs.addAll(webConfig.unix_domain_sockets)
246+
}
247+
socketConfigs.stream().forEach() { socketConfig ->
234248
val udsConnFactories = mutableListOf<ConnectionFactory>()
235249
udsConnFactories.add(HttpConnectionFactory(httpConfig))
236-
if (webConfig.unix_domain_socket.h2c == true) {
250+
if (socketConfig.h2c == true) {
237251
udsConnFactories.add(HTTP2CServerConnectionFactory(httpConfig))
238252
}
239253

240-
val udsConnector = UnixSocketConnector(
241-
server,
242-
null /* executor */,
243-
null /* scheduler */,
244-
null /* buffer pool */,
245-
webConfig.selectors ?: -1,
246-
*udsConnFactories.toTypedArray()
247-
)
248-
udsConnector.setUnixSocket(webConfig.unix_domain_socket.path)
249-
udsConnector.addBean(connectionMetricsCollector.newConnectionListener("http", 0))
250-
udsConnector.name = "uds"
251-
server.addConnector(udsConnector)
254+
if (isJEP380Supported(socketConfig.path)) {
255+
logger.info("Using UnixDomainServerConnector for ${socketConfig.path}")
256+
val udsConnector = UnixDomainServerConnector(
257+
server,
258+
null /* executor */,
259+
null /* scheduler */,
260+
null /* buffer pool */,
261+
webConfig.acceptors ?: -1 ,
262+
webConfig.selectors ?: -1,
263+
*udsConnFactories.toTypedArray()
264+
)
265+
udsConnector.unixDomainPath = File(socketConfig.path).toPath()
266+
udsConnector.addBean(connectionMetricsCollector.newConnectionListener("http", 0))
267+
udsConnector.name = "uds"
268+
server.addConnector(udsConnector)
269+
} else {
270+
val udsConnector = UnixSocketConnector(
271+
server,
272+
null /* executor */,
273+
null /* scheduler */,
274+
null /* buffer pool */,
275+
webConfig.selectors ?: -1,
276+
*udsConnFactories.toTypedArray()
277+
)
278+
udsConnector.setUnixSocket(socketConfig.path)
279+
udsConnector.addBean(connectionMetricsCollector.newConnectionListener("http", 0))
280+
udsConnector.name = "uds"
281+
server.addConnector(udsConnector)
282+
}
252283
}
253284

254285
// TODO(mmihic): Force security handler?
@@ -425,3 +456,16 @@ private fun AbstractHTTP2ServerConnectionFactory.customize(webConfig: WebConfig)
425456
initialStreamRecvWindow = webConfig.jetty_initial_stream_recv_window
426457
}
427458
}
459+
460+
/**
461+
* JEP-380 is supported when running Java 16+ and the provided socket path is non-abstract. Abstract
462+
* socket paths are identified by paths prefixed with an `@` symbol or a null byte.
463+
*/
464+
internal fun isJEP380Supported(
465+
path: String,
466+
javaVersion: Int = JavaVersion.VERSION.major
467+
) : Boolean {
468+
return javaVersion >= 16 &&
469+
!Strings.isNullOrEmpty(path) &&
470+
!Pattern.compile("^@|\u0000").matcher(path).find()
471+
}

misk/src/main/kotlin/misk/web/jetty/WebActionsServlet.kt

+5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import org.eclipse.jetty.http.HttpMethod
2424
import org.eclipse.jetty.server.Request
2525
import org.eclipse.jetty.server.Response
2626
import org.eclipse.jetty.server.ServerConnector
27+
import org.eclipse.jetty.unixdomain.server.UnixDomainServerConnector
2728
import org.eclipse.jetty.unixsocket.server.UnixSocketConnector
2829
import org.eclipse.jetty.websocket.server.JettyServerUpgradeResponse
2930
import org.eclipse.jetty.websocket.server.JettyWebSocketServlet
@@ -119,6 +120,10 @@ internal class WebActionsServlet @Inject constructor(
119120
request = request,
120121
linkLayerLocalAddress = with((request as? Request)?.httpChannel) {
121122
when (this?.connector) {
123+
is UnixDomainServerConnector -> SocketAddress.Unix(
124+
(this.connector as UnixDomainServerConnector).unixDomainPath.toString()
125+
)
126+
122127
is UnixSocketConnector -> SocketAddress.Unix(
123128
(this.connector as UnixSocketConnector).unixSocket
124129
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package misk.web.jetty
2+
3+
import org.assertj.core.api.Assertions.assertThat
4+
import org.junit.jupiter.api.Test
5+
6+
class JettyServiceTest {
7+
8+
@Test
9+
fun isJEP380Supported() {
10+
assertThat(isJEP380Supported("", 16)).isFalse()
11+
assertThat(isJEP380Supported("@socket.sock", 16)).isFalse()
12+
assertThat(isJEP380Supported("\u0000socket.sock", 16)).isFalse()
13+
assertThat(isJEP380Supported("socket.sock", 15)).isFalse()
14+
assertThat(isJEP380Supported("socket.sock", 16)).isTrue()
15+
assertThat(isJEP380Supported("/socket.sock", 16)).isTrue()
16+
}
17+
}

misk/src/test/kotlin/misk/web/jetty/WebActionsServletTest.kt

+25-3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import wisp.client.UnixDomainSocketFactory
2525
import java.io.File
2626
import java.util.UUID
2727
import jakarta.inject.Inject
28+
import org.eclipse.jetty.util.JavaVersion
29+
import org.junit.jupiter.api.Assumptions
2830

2931
@MiskTest(startService = true)
3032
class WebActionsServletTest {
@@ -35,6 +37,7 @@ class WebActionsServletTest {
3537
internal lateinit var jettyService: JettyService
3638

3739
private var socketName: String = "@udstest" + UUID.randomUUID().toString()
40+
private var fileSocketName: String = "/tmp/udstest" + UUID.randomUUID().toString()
3841

3942
@Test
4043
fun networkSocketSuccess() {
@@ -47,7 +50,7 @@ class WebActionsServletTest {
4750
@Test
4851
fun parseNonAsciiHeaders() {
4952
val response = get(
50-
"/potato", false,
53+
"/potato", false, false,
5154
Headers.Builder()
5255
.addUnsafeNonAscii("X-device-name", "Walé Iphone")
5356
.build()
@@ -62,6 +65,13 @@ class WebActionsServletTest {
6265
assertThat(response.header("ActualSocketName")).isEqualTo(socketName)
6366
}
6467

68+
@Test
69+
fun fileUdsSocketSuccess() {
70+
Assumptions.assumeTrue(isJEP380Supported(fileSocketName))
71+
val response = get("/potato", false, true)
72+
assertThat(response.header("ActualSocketName")).isEqualTo(fileSocketName)
73+
}
74+
6575
@Test
6676
fun testPatch404() {
6777
val response = call(
@@ -109,6 +119,7 @@ class WebActionsServletTest {
109119
private fun get(
110120
path: String,
111121
viaUDS: Boolean,
122+
viaFileUDS: Boolean = false,
112123
headers: Headers = Headers.headersOf()
113124
): okhttp3.Response =
114125
with(
@@ -123,7 +134,9 @@ class WebActionsServletTest {
123134
viaUDS -> {
124135
udsCall(get())
125136
}
126-
137+
viaFileUDS -> {
138+
fileUdsCall(get())
139+
}
127140
else -> {
128141
call(get())
129142
}
@@ -141,13 +154,22 @@ class WebActionsServletTest {
141154
.newCall(request.build())
142155
.execute()
143156
}
157+
private fun fileUdsCall(request: Request.Builder): okhttp3.Response {
158+
return OkHttpClient().newBuilder()
159+
.socketFactory(UnixDomainSocketFactory(File(fileSocketName)))
160+
.build()
161+
.newCall(request.build())
162+
.execute()
163+
}
144164

145165
inner class TestModule : KAbstractModule() {
146166
override fun configure() {
147167
install(
148168
WebServerTestingModule(
149169
webConfig = WebServerTestingModule.TESTING_WEB_CONFIG.copy(
150-
unix_domain_socket = WebUnixDomainSocketConfig(path = socketName)
170+
unix_domain_sockets = arrayOf(
171+
WebUnixDomainSocketConfig(path = socketName),
172+
WebUnixDomainSocketConfig(path = fileSocketName))
151173
)
152174
)
153175
)

0 commit comments

Comments
 (0)