GitOrigin-RevId: 59027d7e68139f0f9e058bb7407d21c32adf7ba5
This commit is contained in:
Vladimir Krivosheev
2024-08-19 17:05:43 +02:00
committed by intellij-monorepo-bot
parent 41cb80b5e8
commit a79bc3a1a4
6 changed files with 104 additions and 81 deletions

View File

@@ -2,17 +2,18 @@
package org.jetbrains.ide
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.service
import com.intellij.util.Url
import com.intellij.util.concurrency.annotations.RequiresBlockingContext
import io.netty.bootstrap.Bootstrap
import org.jetbrains.annotations.ApiStatus
import java.net.URLConnection
abstract class BuiltInServerManager {
companion object {
@JvmStatic
fun getInstance(): BuiltInServerManager = ApplicationManager.getApplication().getService(BuiltInServerManager::class.java)
@RequiresBlockingContext
fun getInstance(): BuiltInServerManager = service()
}
abstract val port: Int

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.ide
import com.intellij.notification.Notification
@@ -41,7 +41,7 @@ class BuiltInServerManagerImpl(private val coroutineScope: CoroutineScope) : Bui
private var portOverride: Int? = null
override val port: Int
get() = portOverride ?: server?.port ?: defaultPort
get() = portOverride ?: server?.port ?: getDefaultPort()
override val serverDisposable: Disposable?
get() = server
@@ -118,7 +118,7 @@ class BuiltInServerManagerImpl(private val coroutineScope: CoroutineScope) : Bui
}
try {
server = BuiltInServer.start(firstPort = defaultPort, portsCount = PORTS_COUNT, tryAnyPort = true)
server = BuiltInServer.start(firstPort = getDefaultPort(), portsCount = PORTS_COUNT, tryAnyPort = true)
bindCustomPorts(server!!)
}
catch (e: CancellationException) {
@@ -160,8 +160,12 @@ class BuiltInServerManagerImpl(private val coroutineScope: CoroutineScope) : Bui
}
// the default port will be occupied by the main IDE instance - define the custom default to avoid searching for a free port
private val defaultPort: Int
get() = SystemProperties.getIntProperty(PROPERTY_RPC_PORT, if (ApplicationManager.getApplication().isUnitTestMode) 64463 else BuiltInServerOptions.DEFAULT_PORT)
private fun getDefaultPort(): Int {
return SystemProperties.getIntProperty(
PROPERTY_RPC_PORT,
if (ApplicationManager.getApplication().isUnitTestMode) 64463 else BuiltInServerOptions.DEFAULT_PORT,
)
}
private fun bindCustomPorts(server: BuiltInServer) {
if (ApplicationManager.getApplication().isUnitTestMode) {

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.io
import com.intellij.openapi.Disposable
@@ -17,14 +17,16 @@ import java.lang.invoke.MethodType
import java.net.InetAddress
import java.net.InetSocketAddress
import java.nio.channels.spi.SelectorProvider
import java.util.*
import java.util.concurrent.ThreadFactory
import java.util.concurrent.atomic.AtomicInteger
import kotlin.random.Random
class BuiltInServer private constructor(val eventLoopGroup: EventLoopGroup,
val childEventLoopGroup: EventLoopGroup,
val port: Int,
private val channelRegistrar: ChannelRegistrar) : Disposable {
class BuiltInServer private constructor(
val eventLoopGroup: EventLoopGroup,
val childEventLoopGroup: EventLoopGroup,
val port: Int,
private val channelRegistrar: ChannelRegistrar,
) : Disposable {
val isRunning: Boolean
get() = !channelRegistrar.isEmpty
@@ -33,7 +35,7 @@ class BuiltInServer private constructor(val eventLoopGroup: EventLoopGroup,
// IDEA-120811
if (java.lang.Boolean.parseBoolean(System.getProperty("io.netty.random.id", "true"))) {
System.setProperty("io.netty.machineId", "28:f0:76:ff:fe:16:65:0e")
System.setProperty("io.netty.processId", Random().nextInt(65535).toString())
System.setProperty("io.netty.processId", Random.nextInt(65535).toString())
}
System.setProperty("io.netty.serviceThreadPrefix", "Netty ")
@@ -50,19 +52,6 @@ class BuiltInServer private constructor(val eventLoopGroup: EventLoopGroup,
})
}
private fun setSystemPropertyIfNotConfigured(name: String, @Suppress("SameParameterValue") value: String) {
if (System.getProperty(name) == null) {
System.setProperty(name, value)
}
}
private class BuiltInServerThreadFactory : ThreadFactory {
private val counter = AtomicInteger()
override fun newThread(r: Runnable): Thread =
FastThreadLocalThread(r, "Netty Builtin Server " + counter.incrementAndGet())
}
suspend fun start(firstPort: Int, portsCount: Int, tryAnyPort: Boolean, handler: (() -> ChannelHandler)? = null): BuiltInServer {
val provider = selectorProvider ?: SelectorProvider.provider()
val factory = BuiltInServerThreadFactory()
@@ -70,19 +59,26 @@ class BuiltInServer private constructor(val eventLoopGroup: EventLoopGroup,
val channelRegistrar = ChannelRegistrar()
val bootstrap = createServerBootstrap(eventLoopGroup, eventLoopGroup)
configureChildHandler(bootstrap, channelRegistrar, handler)
val port = bind(firstPort, portsCount, tryAnyPort, bootstrap, channelRegistrar)
return BuiltInServer(eventLoopGroup, eventLoopGroup, port, channelRegistrar)
val port = bind(
firstPort = firstPort,
portsCount = portsCount,
tryAnyPort = tryAnyPort,
bootstrap = bootstrap,
registrar = channelRegistrar,
)
return BuiltInServer(
eventLoopGroup = eventLoopGroup,
childEventLoopGroup = eventLoopGroup,
port = port,
channelRegistrar = channelRegistrar,
)
}
private fun createServerBootstrap(parentEventLoopGroup: EventLoopGroup, childEventLoopGroup: EventLoopGroup): ServerBootstrap {
val bootstrap = ServerBootstrap()
.group(parentEventLoopGroup, childEventLoopGroup)
.channel(NioServerSocketChannel::class.java)
bootstrap.childOption(ChannelOption.TCP_NODELAY, true).childOption(ChannelOption.SO_KEEPALIVE, true)
return bootstrap
}
fun configureChildHandler(bootstrap: ServerBootstrap, channelRegistrar: ChannelRegistrar, channelHandler: (() -> ChannelHandler)? = null) {
fun configureChildHandler(
bootstrap: ServerBootstrap,
channelRegistrar: ChannelRegistrar,
channelHandler: (() -> ChannelHandler)? = null,
) {
val portUnificationServerHandler = if (channelHandler == null) PortUnificationServerHandler() else null
bootstrap.childHandler(object : ChannelInitializer<Channel>(), ChannelHandler {
override fun initChannel(channel: Channel) {
@@ -91,34 +87,6 @@ class BuiltInServer private constructor(val eventLoopGroup: EventLoopGroup,
})
}
private suspend fun bind(firstPort: Int, portsCount: Int, tryAnyPort: Boolean, bootstrap: ServerBootstrap, registrar: ChannelRegistrar): Int {
val address = InetAddress.getByName("127.0.0.1")
val maxPort = (firstPort + portsCount) - 1
for (port in firstPort..maxPort) {
// some antiviral software detects viruses by the fact of accessing these ports, so we should not touch them to appear innocent
if (port == 6953 || port == 6969 || port == 6970) {
continue
}
val future = bootstrap.bind(address, port).asDeferred().await()
if (future.isSuccess) {
registrar.setServerChannel(future.channel(), true)
return port
}
else if (!tryAnyPort && port == maxPort) {
throw future.cause()
}
}
logger<BuiltInServer>().info("Cannot bind to our default range, so, try to bind to any free port")
val future = bootstrap.bind(address, 0).asDeferred().await()
if (future.isSuccess) {
registrar.setServerChannel(future.channel(), true)
return (future.channel().localAddress() as InetSocketAddress).port
}
throw future.cause()
}
fun replaceDefaultHandler(context: ChannelHandlerContext, channelHandler: ChannelHandler) {
context.pipeline().replace(DelegatingHttpRequestHandler::class.java, "replacedDefaultHandler", channelHandler)
}
@@ -132,12 +100,46 @@ class BuiltInServer private constructor(val eventLoopGroup: EventLoopGroup,
}
}
private fun ChannelFuture.asDeferred(): CompletableDeferred<ChannelFuture> {
private suspend fun bind(
firstPort: Int,
portsCount: Int,
tryAnyPort: Boolean,
bootstrap: ServerBootstrap,
registrar: ChannelRegistrar,
): Int {
val address = InetAddress.getByName("127.0.0.1")
val maxPort = (firstPort + portsCount) - 1
for (port in firstPort..maxPort) {
// some antiviral software detects viruses by the fact of accessing these ports, so we should not touch them to appear innocent
if (port == 6953 || port == 6969 || port == 6970) {
continue
}
val future = asDeferred(bootstrap.bind(address, port)).await()
if (future.isSuccess) {
registrar.setServerChannel(future.channel(), true)
return port
}
else if (!tryAnyPort && port == maxPort) {
throw future.cause()
}
}
logger<BuiltInServer>().info("Cannot bind to our default range, so, try to bind to any free port")
val future = asDeferred(bootstrap.bind(address, 0)).await()
if (future.isSuccess) {
registrar.setServerChannel(future.channel(), true)
return (future.channel().localAddress() as InetSocketAddress).port
}
throw future.cause()
}
private fun asDeferred(channelFuture: ChannelFuture): CompletableDeferred<ChannelFuture> {
val deferred = CompletableDeferred<ChannelFuture>()
addListener(object : GenericFutureListener<ChannelFuture> {
channelFuture.addListener(object : GenericFutureListener<ChannelFuture> {
override fun operationComplete(future: ChannelFuture) {
try {
removeListener(this)
channelFuture.removeListener(this)
}
finally {
deferred.complete(future)
@@ -147,6 +149,26 @@ private fun ChannelFuture.asDeferred(): CompletableDeferred<ChannelFuture> {
return deferred
}
private class BuiltInServerThreadFactory : ThreadFactory {
private val counter = AtomicInteger()
override fun newThread(r: Runnable): Thread = FastThreadLocalThread(r, "Netty Builtin Server ${counter.incrementAndGet()}")
}
private fun createServerBootstrap(parentEventLoopGroup: EventLoopGroup, childEventLoopGroup: EventLoopGroup): ServerBootstrap {
return ServerBootstrap()
.group(parentEventLoopGroup, childEventLoopGroup)
.channel(NioServerSocketChannel::class.java)
.childOption(ChannelOption.TCP_NODELAY, true)
.childOption(ChannelOption.SO_KEEPALIVE, true)
}
private fun setSystemPropertyIfNotConfigured(name: String, @Suppress("SameParameterValue") value: String) {
if (System.getProperty(name) == null) {
System.setProperty(name, value)
}
}
private val selectorProvider: SelectorProvider? by lazy {
try {
val aClass = ClassLoader.getSystemClassLoader().loadClass("sun.nio.ch.DefaultSelectorProvider")

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.io;
import com.intellij.openapi.diagnostic.Logger;
@@ -16,8 +16,7 @@ public final class ChannelExceptionHandler extends ChannelHandlerAdapter {
private ChannelExceptionHandler() {
}
@NotNull
public static ChannelHandler getInstance() {
public static @NotNull ChannelHandler getInstance() {
return INSTANCE;
}

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.io;
import io.netty.buffer.ByteBuf;
@@ -40,8 +40,7 @@ public abstract class Decoder extends ChannelInboundHandlerAdapter {
T contentReceived(@NotNull ByteBuf input, @NotNull ChannelHandlerContext context, boolean isCumulateBuffer) throws IOException;
}
@Nullable
protected final <T> T readContent(@NotNull ByteBuf input, @NotNull ChannelHandlerContext context, int contentLength, @NotNull FullMessageConsumer<T> fullMessageConsumer) throws IOException {
protected final @Nullable <T> T readContent(@NotNull ByteBuf input, @NotNull ChannelHandlerContext context, int contentLength, @NotNull FullMessageConsumer<T> fullMessageConsumer) throws IOException {
ByteBuf buffer = getBufferIfSufficient(input, contentLength, context);
if (buffer == null) {
return null;
@@ -63,8 +62,7 @@ public abstract class Decoder extends ChannelInboundHandlerAdapter {
}
}
@Nullable
protected final ByteBuf getBufferIfSufficient(@NotNull ByteBuf input, int requiredLength, @NotNull ChannelHandlerContext context) {
protected final @Nullable ByteBuf getBufferIfSufficient(@NotNull ByteBuf input, int requiredLength, @NotNull ChannelHandlerContext context) {
if (!input.isReadable()) {
return null;
}

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.io;
import com.intellij.openapi.diagnostic.Logger;
@@ -88,8 +88,7 @@ public final class NettyUtil {
return (throwable instanceof ChannelException && message.startsWith("Failed to bind to: "));
}
@NotNull
public static Bootstrap nioClientBootstrap(@NotNull EventLoopGroup eventLoopGroup) {
public static @NotNull Bootstrap nioClientBootstrap(@NotNull EventLoopGroup eventLoopGroup) {
Bootstrap bootstrap = new Bootstrap().group(eventLoopGroup).channel(NioSocketChannel.class);
bootstrap.option(ChannelOption.TCP_NODELAY, true).option(ChannelOption.SO_KEEPALIVE, true);
return bootstrap;