report a peer's port change locally without rewriting it
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful

Keep IConfigurationManager source-compatible for plugin implementers.
This commit is contained in:
2026-09-21 00:46:16 +10:00
parent a9d6c749fb
commit 483c739fb1
4 changed files with 153 additions and 18 deletions
+50 -17
View File
@@ -792,6 +792,43 @@ namespace Emby.Server.Implementations
}
}
/// <summary>
/// Works out what a configuration update means for the ports this process bound at startup.
/// </summary>
/// <param name="boundHttpPort">The HTTP port this process is bound to.</param>
/// <param name="boundHttpsPort">The HTTPS port this process is bound to.</param>
/// <param name="configuredHttpPort">The HTTP port the shared configuration now carries.</param>
/// <param name="configuredHttpsPort">The HTTPS port the shared configuration now carries.</param>
/// <param name="isPortAuthorized">Whether the shared configuration still marks the port as authorized.</param>
/// <param name="isApplyingRemoteInvalidation">Whether this update is another instance's write being applied.</param>
/// <returns>What the update requires of this instance.</returns>
internal static PortChangeOutcome EvaluatePortChange(
int boundHttpPort,
int boundHttpsPort,
int configuredHttpPort,
int configuredHttpsPort,
bool isPortAuthorized,
bool isApplyingRemoteInvalidation)
{
// Nothing is bound yet, so nothing has gone stale.
if (boundHttpPort == 0 || boundHttpsPort == 0)
{
return default;
}
if (configuredHttpPort == boundHttpPort && configuredHttpsPort == boundHttpsPort)
{
return default;
}
// Whoever wrote the change, this process is still listening on a port the configuration no
// longer names, so the pending restart is reported either way. The authorization flag belongs
// to the instance that made the change: it cleared the flag along with the port, and clearing
// it again here would write shared configuration on that instance's behalf and announce it a
// second time.
return new PortChangeOutcome(true, isPortAuthorized && !isApplyingRemoteInvalidation);
}
/// <summary>
/// Called when [configuration updated].
/// </summary>
@@ -799,28 +836,24 @@ namespace Emby.Server.Implementations
/// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
private void OnConfigurationUpdated(object sender, EventArgs e)
{
var requiresRestart = false;
var networkConfiguration = ConfigurationManager.GetNetworkConfiguration();
// Don't do anything if these haven't been set yet, and don't clear the authorization flag on
// behalf of another instance: it already wrote that flag along with the port change, so
// repeating the save here only races it.
if (HttpPort != 0 && HttpsPort != 0 && !ConfigurationInvalidationContext.IsApplyingRemoteInvalidation)
{
// Need to restart if ports have changed
if (networkConfiguration.InternalHttpPort != HttpPort
|| networkConfiguration.InternalHttpsPort != HttpsPort)
{
if (ConfigurationManager.Configuration.IsPortAuthorized)
{
ConfigurationManager.Configuration.IsPortAuthorized = false;
ConfigurationManager.SaveConfiguration();
var portChange = EvaluatePortChange(
HttpPort,
HttpsPort,
networkConfiguration.InternalHttpPort,
networkConfiguration.InternalHttpsPort,
ConfigurationManager.Configuration.IsPortAuthorized,
ConfigurationInvalidationContext.IsApplyingRemoteInvalidation);
requiresRestart = true;
}
}
if (portChange.ClearsPortAuthorization)
{
ConfigurationManager.Configuration.IsPortAuthorized = false;
ConfigurationManager.SaveConfiguration();
}
var requiresRestart = portChange.RequiresRestart;
if (ValidateSslCertificate(networkConfiguration))
{
requiresRestart = true;
@@ -0,0 +1,14 @@
namespace Emby.Server.Implementations
{
/// <summary>
/// What a configuration update carrying different ports requires of the instance reading it.
/// </summary>
/// <param name="RequiresRestart">
/// Whether this process is still bound to a port the shared configuration no longer names, and so has
/// to report a pending restart.
/// </param>
/// <param name="ClearsPortAuthorization">
/// Whether this instance is the one that has to clear the port authorization flag and save it.
/// </param>
internal readonly record struct PortChangeOutcome(bool RequiresRestart, bool ClearsPortAuthorization);
}