Skip to content

Commit 7ccf493

Browse files
committed
Compare complete EndpointConfig sections
1 parent d506f79 commit 7ccf493

File tree

2 files changed

+79
-6
lines changed

2 files changed

+79
-6
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using Microsoft.Extensions.Configuration;
8+
9+
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
10+
{
11+
internal class ConfigSectionClone
12+
{
13+
public ConfigSectionClone(IConfigurationSection configSection)
14+
{
15+
Value = configSection.Value;
16+
17+
// GetChildren() should return an empty IEnumerable instead of null, but we guard against it since it's a public interface.
18+
var children = configSection.GetChildren() ?? Enumerable.Empty<IConfigurationSection>();
19+
Children = children.ToDictionary(child => child.Key, child => new ConfigSectionClone(child));
20+
}
21+
22+
public string Value { get; set; }
23+
public Dictionary<string, ConfigSectionClone> Children { get; set; }
24+
25+
public override bool Equals(object obj)
26+
{
27+
if (!(obj is ConfigSectionClone other))
28+
{
29+
return false;
30+
}
31+
32+
if (Value != other.Value || Children.Count != other.Children.Count)
33+
{
34+
return false;
35+
}
36+
37+
foreach (var kvp in Children)
38+
{
39+
if (!other.Children.TryGetValue(kvp.Key, out var child))
40+
{
41+
return false;
42+
}
43+
44+
if (kvp.Value != child)
45+
{
46+
return false;
47+
}
48+
}
49+
50+
return true;
51+
}
52+
53+
public override int GetHashCode() => HashCode.Combine(Value, Children.Count);
54+
55+
public static bool operator ==(ConfigSectionClone lhs, ConfigSectionClone rhs) => lhs is null ? rhs is null : lhs.Equals(rhs);
56+
public static bool operator !=(ConfigSectionClone lhs, ConfigSectionClone rhs) => !(lhs == rhs);
57+
}
58+
}

src/Servers/Kestrel/Core/src/Internal/ConfigurationReader.cs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,23 +124,38 @@ internal class EndpointDefaults
124124
// }
125125
internal class EndpointConfig
126126
{
127+
private IConfigurationSection _configSection;
128+
private ConfigSectionClone _configSectionClone;
129+
127130
public string Name { get; set; }
128131
public string Url { get; set; }
129132
public HttpProtocols? Protocols { get; set; }
130-
131-
// REVIEW: ConfigSection doesn't seem comparable. If someone changes a custom key and consumes it in
132-
// their Action<EndpointConfiguration>, we won't rebind.
133-
public IConfigurationSection ConfigSection { get; set; }
134133
public CertificateConfig Certificate { get; set; }
135134

135+
// Compare config sections because it's accessible to app developers via an Action<EndpointConfiguration> callback.
136+
// We cannot rely entirely on comparing config sections for equality, because KestrelConfigurationLoader.Reload() sets
137+
// EndpointConfig properties to their default values. If a default value changes, the properties would no longer be equal,
138+
// but the config sections could still be equal.
139+
public IConfigurationSection ConfigSection
140+
{
141+
get => _configSection;
142+
set
143+
{
144+
_configSection = value;
145+
// The IConfigrationSection will mutate, so we need to take a snapshot to compare against later and check for changes.
146+
_configSectionClone = new ConfigSectionClone(value);
147+
}
148+
}
149+
136150
public override bool Equals(object obj) =>
137151
obj is EndpointConfig other &&
138152
Name == other.Name &&
139153
Url == other.Url &&
140154
(Protocols ?? ListenOptions.DefaultHttpProtocols) == (other.Protocols ?? ListenOptions.DefaultHttpProtocols) &&
141-
Certificate == other.Certificate;
155+
Certificate == other.Certificate &&
156+
_configSectionClone == other._configSectionClone;
142157

143-
public override int GetHashCode() => HashCode.Combine(Name, Url, Protocols ?? ListenOptions.DefaultHttpProtocols, Certificate);
158+
public override int GetHashCode() => HashCode.Combine(Name, Url, Protocols ?? ListenOptions.DefaultHttpProtocols, Certificate, _configSectionClone);
144159

145160
public static bool operator ==(EndpointConfig lhs, EndpointConfig rhs) => lhs is null ? rhs is null : lhs.Equals(rhs);
146161
public static bool operator !=(EndpointConfig lhs, EndpointConfig rhs) => !(lhs == rhs);

0 commit comments

Comments
 (0)