mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[gradle][eel][IDEA-382138] introduce GradleEelProxyManager as a replacement for GradleDebuggerProxyManager
GitOrigin-RevId: 9a4f5c455f0836ecf1982887ad2c51165ff3f146
This commit is contained in:
committed by
intellij-monorepo-bot
parent
9f390a8349
commit
db6a582357
+44
-47
@@ -1,5 +1,5 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.plugins.gradle.service.task.debugger
|
||||
package org.jetbrains.plugins.gradle.service.execution.eel
|
||||
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId
|
||||
@@ -12,82 +12,64 @@ import com.intellij.platform.eel.EelTunnelsApi
|
||||
import com.intellij.platform.eel.provider.getEelDescriptor
|
||||
import com.intellij.platform.eel.provider.utils.forwardLocalServer
|
||||
import com.intellij.platform.util.coroutines.childScope
|
||||
import com.intellij.util.net.NetUtils
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.cancel
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
/**
|
||||
* A project-level service that could provide a proxy, that will be available during execution of an [ExternalSystemTaskId].
|
||||
* A project-level service that could provide a proxy, that will be available during execution of an
|
||||
* [com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId].
|
||||
* When the task is complete, the proxy server will be stopped.
|
||||
*/
|
||||
@Service(Service.Level.PROJECT)
|
||||
internal class GradleDebuggerProxyManager(
|
||||
internal class GradleEelProxyManager(
|
||||
private val project: Project,
|
||||
private val cs: CoroutineScope,
|
||||
) {
|
||||
|
||||
private class Mapping {
|
||||
|
||||
private data class Entry(val localPort: Int, val remotePort: Int)
|
||||
|
||||
private val entries: MutableList<Entry> = mutableListOf()
|
||||
|
||||
fun add(localPort: Int, remotePort: Int) {
|
||||
entries.add(Entry(localPort, remotePort))
|
||||
}
|
||||
|
||||
fun getLocalToRemote(remotePort: Int): Int? = entries.find { it.remotePort == remotePort }?.localPort
|
||||
companion object {
|
||||
fun getInstance(project: Project): GradleEelProxyManager = project.getService(GradleEelProxyManager::class.java)
|
||||
}
|
||||
|
||||
private val scopes: ConcurrentHashMap<Long, CoroutineScope> = ConcurrentHashMap()
|
||||
private val mappings: ConcurrentHashMap<Long, Mapping> = ConcurrentHashMap()
|
||||
|
||||
/**
|
||||
* This proxy pipe will be used to establish the management connection between IDEA and our debugging-related tooling.
|
||||
*
|
||||
* @param taskId the corresponding task ID.
|
||||
* @param dispatchLocalPort the port provided by `ExternalSystemRunnableState.getForkSocket()`. On this port an instance of
|
||||
* `ForkedDebuggerThread` will wait for the connection from the daemon.
|
||||
* @return an opened port on the remote side.
|
||||
*/
|
||||
fun launchProxy(taskId: ExternalSystemTaskId, dispatchLocalPort: Int): Int {
|
||||
val eelDescriptor = project.getEelDescriptor()
|
||||
return eelDescriptor.openSink(taskId, dispatchLocalPort)
|
||||
}
|
||||
|
||||
/**
|
||||
* This proxy pipe will be used to establish the connection between IDEA and the debugger itself.
|
||||
* When a connection between our init script and IDEA will be established, the init script will modify an execution-related task and
|
||||
* start a debugger on this port.
|
||||
* After IDEA receive a heartbeat from the daemon, we could establish a connection with the remote debugger
|
||||
* on 127.0.0.1:[getReverseProxyLocalSink]
|
||||
* There is no need to explicitly receive/return the local port, because it could be looked up later based on the REMOTE port via
|
||||
* [getReverseProxyLocalSink].
|
||||
* Set up a proxy between %localhost%:[localPort] and %remote_localhost%:[@result].
|
||||
*
|
||||
* @param taskId the corresponding task ID.
|
||||
* @param localPort a local port that should be used to forward traffic to.
|
||||
* @return an opened port on the **remote** side.
|
||||
*/
|
||||
fun setupReverseProxy(taskId: ExternalSystemTaskId): Int {
|
||||
val localSink = NetUtils.findAvailableSocketPort()
|
||||
val eelDescriptor = project.getEelDescriptor()
|
||||
return eelDescriptor.openSink(taskId, localSink)
|
||||
fun launchReverseProxy(taskId: ExternalSystemTaskId, localPort: Int): Int = project.getEelDescriptor()
|
||||
.openLocalSink(taskId, localPort)
|
||||
|
||||
/**
|
||||
* Get mapping between remote and local port.
|
||||
*
|
||||
* @param remotePort a remote port
|
||||
* @return a local port that could be used to forward traffic to %remoteHost%:[remotePort]
|
||||
*/
|
||||
fun getLocalToRemotePort(remotePort: Int): Int {
|
||||
return mappings.values
|
||||
.firstNotNullOfOrNull { it.getLocalToRemote(remotePort) }
|
||||
?: throw IllegalArgumentException("No local sink found for the remote port $remotePort")
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a local port that corresponds to the remote port.
|
||||
* Get mapping between local and remote port.
|
||||
*
|
||||
* @param reverseProxyRemoteSink a remote port
|
||||
* @return a local port that could be used to access [reverseProxyRemoteSink]
|
||||
* @param localPort a local port
|
||||
* @return a remote port that could be used to access %127.0.0.1%:[localPort]
|
||||
*/
|
||||
fun getReverseProxyLocalSink(reverseProxyRemoteSink: Int): Int {
|
||||
fun getRemoteToLocalPort(localPort: Int): Int {
|
||||
return mappings.values
|
||||
.firstNotNullOfOrNull { it.getLocalToRemote(reverseProxyRemoteSink) }
|
||||
?: throw IllegalArgumentException("No local sink found for remote port $reverseProxyRemoteSink")
|
||||
.firstNotNullOfOrNull { it.getRemoteToLocal(localPort) }
|
||||
?: throw IllegalArgumentException("No remote sink found for the local port $localPort")
|
||||
}
|
||||
|
||||
private fun EelDescriptor.openSink(taskId: ExternalSystemTaskId, localPort: Int): Int {
|
||||
private fun EelDescriptor.openLocalSink(taskId: ExternalSystemTaskId, localPort: Int): Int {
|
||||
val remoteHostAddress = EelTunnelsApi.HostAddress.Builder()
|
||||
.connectionTimeout(90.seconds)
|
||||
.build()
|
||||
@@ -127,4 +109,19 @@ internal class GradleDebuggerProxyManager(
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private class Mapping {
|
||||
|
||||
private data class Entry(val localPort: Int, val remotePort: Int)
|
||||
|
||||
private val entries: MutableList<Entry> = mutableListOf()
|
||||
|
||||
fun add(localPort: Int, remotePort: Int) {
|
||||
entries.add(Entry(localPort, remotePort))
|
||||
}
|
||||
|
||||
fun getLocalToRemote(remotePort: Int): Int? = entries.find { it.remotePort == remotePort }?.localPort
|
||||
|
||||
fun getRemoteToLocal(localPort: Int): Int? = entries.find { it.localPort == localPort }?.remotePort
|
||||
}
|
||||
}
|
||||
+22
-12
@@ -8,8 +8,10 @@ import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.platform.eel.provider.LocalEelDescriptor
|
||||
import com.intellij.platform.eel.provider.getEelDescriptor
|
||||
import com.intellij.util.net.NetUtils
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.plugins.gradle.service.execution.GradleExecutionContext
|
||||
import org.jetbrains.plugins.gradle.service.execution.eel.GradleEelProxyManager
|
||||
import org.jetbrains.plugins.gradle.settings.GradleExecutionSettings
|
||||
|
||||
@ApiStatus.Internal
|
||||
@@ -19,7 +21,7 @@ object GradleDebuggerSupport {
|
||||
private const val DEBUGGER_AGENT_JVM_ARG_FORMAT = "-D${DEBUGGER_AGENT_SINK_PORT_SYS_PROP}=%d"
|
||||
|
||||
/**
|
||||
* See the documentation entry for [GradleDebuggerProxyManager.getReverseProxyLocalSink].
|
||||
* See the documentation entry for [GradleEelProxyManager.getLocalToRemotePort].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getDebuggeeLocalPort(project: Project, mayBeRemotePort: String?): String? {
|
||||
@@ -30,13 +32,13 @@ object GradleDebuggerSupport {
|
||||
if (projectEelDescriptor == LocalEelDescriptor) {
|
||||
return mayBeRemotePort
|
||||
}
|
||||
val proxyManager = project.getService(GradleDebuggerProxyManager::class.java)
|
||||
val localSinkToDebuggee = proxyManager.getReverseProxyLocalSink(mayBeRemotePort.toInt())
|
||||
val proxyManager = GradleEelProxyManager.getInstance(project)
|
||||
val localSinkToDebuggee = proxyManager.getLocalToRemotePort(mayBeRemotePort.toInt())
|
||||
return localSinkToDebuggee.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation entry for [GradleDebuggerProxyManager.setupReverseProxy].
|
||||
* See the documentation entry for [GradleEelProxyManager.launchReverseProxy].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun setupDebuggerProxy(context: GradleExecutionContext, settings: GradleExecutionSettings) {
|
||||
@@ -45,29 +47,37 @@ object GradleDebuggerSupport {
|
||||
}
|
||||
|
||||
if (settings.isExecutionDebugRequired()) {
|
||||
val proxyManager = context.project.getService(GradleDebuggerProxyManager::class.java)
|
||||
val proxyManager = GradleEelProxyManager.getInstance(context.project)
|
||||
settings.replacePortWithProxy(proxyManager, context.taskId, DEBUGGER_DISPATCH_PORT_KEY)
|
||||
|
||||
// this port will be used by the debugger agent ON THE REMOTE SIDE.
|
||||
val debuggerAgentSink = proxyManager.setupReverseProxy(context.taskId)
|
||||
// This port will be used by the debugger agent ON THE REMOTE SIDE.
|
||||
// This proxy pipe will be used to establish the connection between IDEA and the debugger itself.
|
||||
// When a connection between our init script and IDEA will be established, the init script will modify an execution-related task and
|
||||
// start a debugger on this port.
|
||||
// After IDEA receive a heartbeat from the daemon, we could establish a connection with the remote debugger
|
||||
// on 127.0.0.1:[getLocalToRemotePort]
|
||||
val debuggerAgentSink = proxyManager.launchReverseProxy(context.taskId, NetUtils.findAvailableSocketPort())
|
||||
val debugArgument = DEBUGGER_AGENT_JVM_ARG_FORMAT.format(debuggerAgentSink)
|
||||
settings.withArguments(debugArgument)
|
||||
}
|
||||
|
||||
if (settings.isScriptDebugRequired()) {
|
||||
val proxyManager = context.project.getService(GradleDebuggerProxyManager::class.java)
|
||||
val proxyManager = GradleEelProxyManager.getInstance(context.project)
|
||||
settings.replacePortWithProxy(proxyManager, context.taskId, BUILD_PROCESS_DEBUGGER_PORT_KEY)
|
||||
}
|
||||
}
|
||||
|
||||
private fun GradleExecutionSettings.replacePortWithProxy(
|
||||
proxyManager: GradleDebuggerProxyManager,
|
||||
proxyManager: GradleEelProxyManager,
|
||||
taskId: ExternalSystemTaskId,
|
||||
portKey: Key<Int>,
|
||||
) {
|
||||
val sourcePort = getUserData(portKey)
|
||||
?: throw IllegalStateException("The source port from key $portKey should not be null")
|
||||
val proxyPort = proxyManager.launchProxy(taskId, sourcePort)
|
||||
// the port provided by `ExternalSystemRunnableState.getForkSocket()`
|
||||
// On this port an instance of `ForkedDebuggerThread` will wait for the connection from the daemon
|
||||
val sourceLocalPort = getUserData(portKey)
|
||||
?: throw IllegalStateException("The source port from key $portKey should not be null")
|
||||
|
||||
val proxyPort = proxyManager.launchReverseProxy(taskId, sourceLocalPort)
|
||||
|
||||
putUserData(portKey, proxyPort)
|
||||
putUserData(DEBUGGER_DISPATCH_ADDR_KEY, LOCALHOST)
|
||||
|
||||
Reference in New Issue
Block a user