fix(client): Client.Shared EndpointSelector uses the shared OpcUaEndpointUrlRewriter (#812)
Client.Shared now project-references Driver.OpcUaClient.Contracts (Core.Abstractions only, no NuGet/SDK; pinned by ContractsIsDependencyLightTests) and delegates the host/port rewrite to OpcUaEndpointUrlRewriter, deleting its own copy. The two copies differed; reconciled toward the client so its pinned behaviour is unchanged (EndpointSelectorRewriteParityTests, written green against the old copy): - no-op rewrite (same host case-insensitively + same port) now returns the advertised string verbatim in the shared rewriter too (it re-serialised: added a trailing slash and case-folded a case-only host difference); - a real rewrite serialises via AbsoluteUri, not Uri.ToString(), which un-escaped the advertised path (/x%20y became /x y) in the driver + browser; - unusable URLs: the rewriter still falls back to the configured URL (driver/browser); the client keeps its loud failure as call-site validation (null -> InvalidOperation, unparseable -> UriFormatException).
This commit is contained in:
+18
-10
@@ -5,7 +5,9 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient;
|
||||
/// OpcUaClient driver family. A server reports its GetEndpoints results with ITS view of its own address; behind
|
||||
/// NAT, a port map, a container port publish or a proxy that address is unreachable from here (or bypasses the
|
||||
/// proxy). Shared by the runtime driver (Gitea #676) and the AdminUI live-browse picker (Gitea #806, which used
|
||||
/// the advertised URL unchanged and failed DNS on a published-port fixture the driver handled).
|
||||
/// the advertised URL unchanged and failed DNS on a published-port fixture the driver handled), and — since
|
||||
/// Gitea #812 — by the Client CLI/UI's <c>EndpointSelector</c> (Client.Shared), whose own copy of this rule it
|
||||
/// replaced.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Pure string/<see cref="Uri"/> logic, deliberately in <c>.Contracts</c>: it is not session-typed, so it must not
|
||||
@@ -18,8 +20,10 @@ public static class OpcUaEndpointUrlRewriter
|
||||
/// <summary>
|
||||
/// The address to open the session on. When the advertised URL's scheme matches the configured one, the
|
||||
/// configured host AND port (when it names one) replace the advertised ones and the advertised path is kept —
|
||||
/// the SDK's own <c>CoreClientUtils.SelectEndpoint</c> rule. A different scheme is a different transport and is
|
||||
/// left alone; an unparseable advertised URL falls back to the configured one.
|
||||
/// the SDK's own <c>CoreClientUtils.SelectEndpoint</c> rule. When host (case-insensitively) and port already
|
||||
/// match, the advertised string is returned verbatim. A different scheme is a different transport and is left
|
||||
/// alone; an unparseable advertised URL falls back to the configured one (callers that must fail loudly
|
||||
/// instead, like the Client CLI/UI, validate before calling).
|
||||
/// </summary>
|
||||
/// <param name="advertisedUrl">The <c>EndpointUrl</c> the server returned from GetEndpoints.</param>
|
||||
/// <param name="configuredUrl">The URL the operator configured (the one GetEndpoints was sent to).</param>
|
||||
@@ -32,12 +36,16 @@ public static class OpcUaEndpointUrlRewriter
|
||||
return configuredUrl;
|
||||
if (!string.Equals(advertised.Scheme, configured.Scheme, StringComparison.OrdinalIgnoreCase)) return advertisedUrl;
|
||||
// A port-less configured URL (Uri.Port == -1 for opc.tcp) keeps the advertised port — copying -1 would drop it
|
||||
// and the SDK would fall back to 4840 (review of 3b88056f; same fix as the client's EndpointSelector).
|
||||
var builder = new UriBuilder(advertised)
|
||||
{
|
||||
Host = configured.DnsSafeHost,
|
||||
Port = configured.Port > 0 ? configured.Port : advertised.Port,
|
||||
};
|
||||
return builder.Uri.ToString();
|
||||
// and the SDK would fall back to 4840 (review of 3b88056f).
|
||||
var port = configured.Port > 0 ? configured.Port : advertised.Port;
|
||||
// Nothing to rewrite: return the advertised string verbatim rather than a re-serialised copy (no added
|
||||
// trailing slash, no case-folding of a host that only differs in case — DNS is case-insensitive). This is the
|
||||
// Client CLI/UI's long-standing rule, kept when its own copy was folded in here (#812).
|
||||
if (string.Equals(advertised.DnsSafeHost, configured.DnsSafeHost, StringComparison.OrdinalIgnoreCase)
|
||||
&& advertised.Port == port)
|
||||
return advertisedUrl;
|
||||
var builder = new UriBuilder(advertised) { Host = configured.DnsSafeHost, Port = port };
|
||||
// AbsoluteUri, not ToString(): ToString() un-escapes, turning an advertised "/x%20y" into "/x y" (#812).
|
||||
return builder.Uri.AbsoluteUri;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user