|
14 | 14 | using System.Threading;
|
15 | 15 | using System.Threading.Tasks;
|
16 | 16 | using Microsoft.DotNet.RemoteExecutor;
|
| 17 | +using Microsoft.DotNet.XUnitExtensions; |
17 | 18 | using Xunit;
|
18 | 19 | using Xunit.Abstractions;
|
19 | 20 |
|
@@ -71,13 +72,12 @@ protected HttpMetricsTestBase(ITestOutputHelper output) : base(output)
|
71 | 72 | }
|
72 | 73 |
|
73 | 74 |
|
74 |
| - private static void VerifyPeerAddress(KeyValuePair<string, object?>[] tags) |
| 75 | + private static void VerifyPeerAddress(KeyValuePair<string, object?>[] tags, IPAddress[] validPeerAddresses = null) |
75 | 76 | {
|
76 | 77 | string ipString = (string)tags.Single(t => t.Key == "network.peer.address").Value;
|
| 78 | + validPeerAddresses ??= [IPAddress.Loopback.MapToIPv6(), IPAddress.Loopback, IPAddress.IPv6Loopback]; |
77 | 79 | IPAddress ip = IPAddress.Parse(ipString);
|
78 |
| - Assert.True(ip.Equals(IPAddress.Loopback.MapToIPv6()) || |
79 |
| - ip.Equals(IPAddress.Loopback) || |
80 |
| - ip.Equals(IPAddress.IPv6Loopback)); |
| 80 | + Assert.Contains(ip, validPeerAddresses); |
81 | 81 | }
|
82 | 82 |
|
83 | 83 |
|
@@ -126,24 +126,29 @@ protected static void VerifyActiveRequests(string instrumentName, long measureme
|
126 | 126 | Assert.Equal(method, tags.Single(t => t.Key == "http.request.method").Value);
|
127 | 127 | }
|
128 | 128 |
|
129 |
| - protected static void VerifyOpenConnections(string actualName, object measurement, KeyValuePair<string, object?>[] tags, long expectedValue, Uri uri, Version? protocolVersion, string state) |
| 129 | + protected static void VerifyOpenConnections(string actualName, object measurement, KeyValuePair<string, object?>[] tags, long expectedValue, Uri uri, Version? protocolVersion, string state, IPAddress[] validPeerAddresses = null) |
130 | 130 | {
|
131 | 131 | Assert.Equal(InstrumentNames.OpenConnections, actualName);
|
132 | 132 | Assert.Equal(expectedValue, Assert.IsType<long>(measurement));
|
133 | 133 | VerifySchemeHostPortTags(tags, uri);
|
134 | 134 | VerifyTag(tags, "network.protocol.version", GetVersionString(protocolVersion));
|
135 | 135 | VerifyTag(tags, "http.connection.state", state);
|
136 |
| - VerifyPeerAddress(tags); |
| 136 | + VerifyPeerAddress(tags, validPeerAddresses); |
137 | 137 | }
|
138 | 138 |
|
139 |
| - protected static void VerifyConnectionDuration(string instrumentName, object measurement, KeyValuePair<string, object?>[] tags, Uri uri, Version? protocolVersion) |
| 139 | + protected static void VerifyConnectionDuration(string instrumentName, object measurement, KeyValuePair<string, object?>[] tags, Uri uri, Version? protocolVersion, IPAddress[] validPeerAddresses = null) |
140 | 140 | {
|
141 | 141 | Assert.Equal(InstrumentNames.ConnectionDuration, instrumentName);
|
142 | 142 | double value = Assert.IsType<double>(measurement);
|
143 |
| - Assert.InRange(value, double.Epsilon, 60); |
| 143 | + |
| 144 | + // This flakes for remote requests on CI. |
| 145 | + if (validPeerAddresses is null) |
| 146 | + { |
| 147 | + Assert.InRange(value, double.Epsilon, 60); |
| 148 | + } |
144 | 149 | VerifySchemeHostPortTags(tags, uri);
|
145 | 150 | VerifyTag(tags, "network.protocol.version", GetVersionString(protocolVersion));
|
146 |
| - VerifyPeerAddress(tags); |
| 151 | + VerifyPeerAddress(tags, validPeerAddresses); |
147 | 152 | }
|
148 | 153 |
|
149 | 154 | protected static void VerifyTimeInQueue(string instrumentName, object measurement, KeyValuePair<string, object?>[] tags, Uri uri, Version? protocolVersion, string method = "GET")
|
@@ -362,6 +367,41 @@ public Task RequestDuration_Success_Recorded(string method, HttpStatusCode statu
|
362 | 367 | });
|
363 | 368 | }
|
364 | 369 |
|
| 370 | + [OuterLoop("Uses external server.")] |
| 371 | + [ConditionalFact] |
| 372 | + public async Task ExternalServer_DurationMetrics_Recorded() |
| 373 | + { |
| 374 | + if (UseVersion == HttpVersion.Version30) |
| 375 | + { |
| 376 | + throw new SkipTestException("No remote HTTP/3 server available for testing."); |
| 377 | + } |
| 378 | + |
| 379 | + using InstrumentRecorder<double> requestDurationRecorder = SetupInstrumentRecorder<double>(InstrumentNames.RequestDuration); |
| 380 | + using InstrumentRecorder<double> connectionDurationRecorder = SetupInstrumentRecorder<double>(InstrumentNames.ConnectionDuration); |
| 381 | + using InstrumentRecorder<long> openConnectionsRecorder = SetupInstrumentRecorder<long>(InstrumentNames.OpenConnections); |
| 382 | + |
| 383 | + Uri uri = UseVersion == HttpVersion.Version11 |
| 384 | + ? Test.Common.Configuration.Http.RemoteHttp11Server.EchoUri |
| 385 | + : Test.Common.Configuration.Http.RemoteHttp2Server.EchoUri; |
| 386 | + IPAddress[] addresses = await Dns.GetHostAddressesAsync(uri.Host); |
| 387 | + addresses = addresses.Union(addresses.Select(a => a.MapToIPv6())).ToArray(); |
| 388 | + |
| 389 | + using (HttpMessageInvoker client = CreateHttpMessageInvoker()) |
| 390 | + { |
| 391 | + using HttpRequestMessage request = new(HttpMethod.Get, uri) { Version = UseVersion }; |
| 392 | + request.Headers.ConnectionClose = true; |
| 393 | + using HttpResponseMessage response = await SendAsync(client, request); |
| 394 | + await response.Content.LoadIntoBufferAsync(); |
| 395 | + await WaitForEnvironmentTicksToAdvance(); |
| 396 | + } |
| 397 | + |
| 398 | + VerifyRequestDuration(Assert.Single(requestDurationRecorder.GetMeasurements()), uri, UseVersion, 200, "GET"); |
| 399 | + Measurement<double> cd = Assert.Single(connectionDurationRecorder.GetMeasurements()); |
| 400 | + VerifyConnectionDuration(InstrumentNames.ConnectionDuration, cd.Value, cd.Tags.ToArray(), uri, UseVersion, addresses); |
| 401 | + Measurement<long> oc = openConnectionsRecorder.GetMeasurements().First(); |
| 402 | + VerifyOpenConnections(InstrumentNames.OpenConnections, oc.Value, oc.Tags.ToArray(), 1, uri, UseVersion, "idle", addresses); |
| 403 | + } |
| 404 | + |
365 | 405 | [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
|
366 | 406 | public async Task RequestDuration_HttpTracingEnabled_RecordedWhileRequestActivityRunning()
|
367 | 407 | {
|
|
0 commit comments