This commit is contained in:
Florian Rappl
2026-09-04 11:52:21 +02:00
parent 7af2e954c7
commit 0140749753
6 changed files with 64 additions and 10 deletions

View File

@@ -4,6 +4,7 @@
- Updated dependencies
- Fixed single instance handling on macOS (#1040)
- Fixed slow socket bridge startup by binding to an explicit loopback address (#1103)
- Fixed socket bridge connection when a system proxy is configured (#1105)
- Fixed electron-builder using the host RID instead of the target RID (#1097)
- Fixed cross-compilation behavior on same platform (#1098) @epsnm

View File

@@ -15,6 +15,7 @@
internal const int DefaultSocketPort = 8000;
internal const int DefaultWebPort = 8001;
internal const string ElectronPortArgumentName = "electronPort";
internal const string ElectronHostArgumentName = "electronHost";
internal const string ElectronPidArgumentName = "electronPID";
internal const string ElectronAuthTokenArgumentName = "electronAuthToken";
@@ -31,6 +32,11 @@
public static int? ElectronSocketPort { get; internal set; }
/// <summary>
/// The loopback address the Electron socket bridge is listening on.
/// </summary>
public static string ElectronSocketHost { get; internal set; }
public static int? AspNetWebPort { get; internal set; }
public static StartupMethod StartupMethod { get; internal set; }

View File

@@ -18,7 +18,7 @@
[Localizable(false)]
internal class ElectronProcessActive : ElectronProcessBase
{
private readonly Regex extractor = new Regex("^Electron Socket: listening on port (\\d+) at .* using ([a-f0-9]+)$");
private readonly Regex extractor = new Regex("^Electron Socket: listening on port (\\d+) at (\\S+) using ([a-f0-9]+)$");
private readonly bool isUnpackaged;
private readonly string electronBinaryName;
@@ -179,11 +179,13 @@
if (match?.Success ?? false)
{
var port = int.Parse(match.Groups[1].Value);
var token = match.Groups[2].Value;
var host = match.Groups[2].Value;
var token = match.Groups[3].Value;
this.process.LineReceived -= Read_SocketIO_Parameters;
ElectronNetRuntime.ElectronAuthToken = token;
ElectronNetRuntime.ElectronSocketPort = port;
ElectronNetRuntime.ElectronSocketHost = host;
tcs.SetResult();
}
}

View File

@@ -16,7 +16,19 @@
{
this.socketPort = socketPort;
this.authorization = authorization;
this.socketUrl = $"http://localhost:{this.socketPort}";
this.socketUrl = $"http://{FormatHost(ElectronNetRuntime.ElectronSocketHost)}:{this.socketPort}";
}
// The Electron host reports the loopback address it is actually listening on; only
// when it is unknown we have to fall back to the ambiguous hostname.
private static string FormatHost(string socketHost)
{
if (string.IsNullOrWhiteSpace(socketHost))
{
return "localhost";
}
return socketHost.Contains(':') ? $"[{socketHost}]" : socketHost;
}
public int SocketPort => this.socketPort;

View File

@@ -94,8 +94,20 @@
}
}
var pidArg = argsList.FirstOrDefault(e => e.Contains(ElectronNetRuntime.ElectronPidArgumentName, StringComparison.OrdinalIgnoreCase));
var hostArg = argsList.FirstOrDefault(e => e.Contains(ElectronNetRuntime.ElectronHostArgumentName, StringComparison.OrdinalIgnoreCase));
if (hostArg != null)
{
var parts = hostArg.Split('=', StringSplitOptions.TrimEntries);
if (parts.Length > 1 && !string.IsNullOrWhiteSpace(parts[1]))
{
ElectronNetRuntime.ElectronSocketHost = parts[1];
Console.WriteLine("Use Electron Host: " + parts[1]);
}
}
var pidArg = argsList.FirstOrDefault(e => e.Contains(ElectronNetRuntime.ElectronPidArgumentName, StringComparison.OrdinalIgnoreCase));
if (pidArg != null)
{
var parts = pidArg.Split('=', StringSplitOptions.TrimEntries);

View File

@@ -175,6 +175,7 @@ function getForwardedArgs() {
if (cleaned.startsWith('remote-debugging-port')) return false;
// We add /electronPort ourselves later
if (cleaned.startsWith('electronPort=')) return false;
if (cleaned.startsWith('electronHost=')) return false;
if (cleaned.startsWith('electronWebPort=')) return false;
return true;
});
@@ -295,7 +296,6 @@ function startSocketApiBridge(port) {
// otherwise the Windows Firewall will be triggered
console.debug('Electron Socket: starting...');
server = createServer();
const host = !port ? '127.0.0.1' : 'localhost';
let hostHook;
io = new Server({
pingTimeout: 60000, // in ms, default is 5000
@@ -303,16 +303,35 @@ function startSocketApiBridge(port) {
});
io.attach(server);
server.listen(port, host);
// Never bind to the 'localhost' hostname: it may resolve to ::1 and 127.0.0.1 in any
// order, so server and client can end up on different stacks - which costs a failed
// connection attempt (or a DNS lookup) on every startup.
const hostCandidates = ['127.0.0.1', '::1'];
let hostIndex = 0;
server.on('error', (error) => {
const isUnavailable = error.code === 'EADDRNOTAVAIL' || error.code === 'EAFNOSUPPORT' || error.code === 'EINVAL';
if (isUnavailable && hostIndex + 1 < hostCandidates.length) {
console.warn(`Electron Socket: cannot bind to ${hostCandidates[hostIndex]} (${error.code}), falling back to ${hostCandidates[hostIndex + 1]}.`);
hostIndex++;
server.listen(port, hostCandidates[hostIndex]);
return;
}
console.error('Electron Socket: ' + error.message);
});
server.listen(port, hostCandidates[hostIndex]);
server.on('listening', function () {
const addr = server.address();
console.info(`Electron Socket: listening on port ${addr.port} at ${addr.address} using ${authToken}`);
// Now that socket connection is established, we can guarantee port will not be open for portscanner
if (unpackedelectron) {
startAspCoreBackendUnpackaged(addr.port);
startAspCoreBackendUnpackaged(addr.port, addr.address);
} else if (!unpackeddotnet && !dotnetpacked) {
startAspCoreBackend(addr.port);
startAspCoreBackend(addr.port, addr.address);
}
});
@@ -416,7 +435,7 @@ function startSocketApiBridge(port) {
});
}
function startAspCoreBackend(electronPort) {
function startAspCoreBackend(electronPort, electronHost) {
startBackend();
function startBackend() {
@@ -425,6 +444,7 @@ function startAspCoreBackend(electronPort) {
const parameters = [
envParam,
`/electronPort=${electronPort}`,
`/electronHost=${electronHost}`,
`/electronPID=${process.pid}`,
`/electronAuthToken=${authToken}`,
// forward user supplied args (avoid duplicate environment)
@@ -447,7 +467,7 @@ function startAspCoreBackend(electronPort) {
}
}
function startAspCoreBackendUnpackaged(electronPort) {
function startAspCoreBackendUnpackaged(electronPort, electronHost) {
startBackend();
function startBackend() {
@@ -456,6 +476,7 @@ function startAspCoreBackendUnpackaged(electronPort) {
const parameters = [
envParam,
`/electronPort=${electronPort}`,
`/electronHost=${electronHost}`,
`/electronPID=${process.pid}`,
`/electronAuthToken=${authToken}`,
...forwardedArgs.filter(a => !(envParam && a.startsWith('--environment=')))