Skip to content

Commit 81034fa

Browse files
Send Warm-up request in iisexpress integration tests (#7321)
## Summary of changes We have identified some flaky tests when running under iisexpress. To address part of the issue, we have already scrubbed certain connection headers from the snapshots, as the presence of specific flags from a given request may not be consistent. This inconsistency is influenced by test execution order and cannot always be predicted. Additionally, in ASM, certain tags, such as the WAF version, are sent during the first request. In other frameworks, this is typically handled by warm-up calls, which helps avoid such issues. However, iisexpress tests currently do not include warm-up calls. Introducing a warm-up call would help produce more predictable test results and resolve some of the inconsistencies we have been encountering. ## Reason for change ## Implementation details ## Test coverage ## Other details <!-- Fixes #{issue} --> <!-- ⚠️ Note: where possible, please obtain 2 approvals prior to merging. Unless CODEOWNERS specifies otherwise, for external teams it is typically best to have one review from a team member, and one review from apm-dotnet. Trivial changes do not require 2 reviews. -->
1 parent e97f67c commit 81034fa

File tree

29 files changed

+147
-82
lines changed

29 files changed

+147
-82
lines changed

tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AspNet/OwinIisWebApi2Tests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ await Verifier.Verify(spans, settings)
145145
}
146146

147147
// OWIN can only run in integrated mode
148-
public Task InitializeAsync() => _iisFixture.TryStartIis(this, IisAppType.AspNetIntegrated);
148+
public Task InitializeAsync() => _iisFixture.TryStartIis(this, IisAppType.AspNetIntegrated, true, "/api/environment");
149149

150150
public Task DisposeAsync() => Task.CompletedTask;
151151
}

tracer/test/Datadog.Trace.Security.IntegrationTests/AspNetWebForms.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public async Task TestBlockingRedirectInvalidStatusCode(int ruleTriggerStatusCod
4040
const string url = "/";
4141

4242
var settings = VerifyHelper.GetSpanVerifierSettings(ruleTriggerStatusCode, returnedStatusCode);
43+
FilterConnectionHeader(settings);
4344
var userAgent = "Canary/v3_" + ruleTriggerStatusCode;
4445

4546
var minDateTime = DateTime.UtcNow;
@@ -112,6 +113,7 @@ public Task TestSecurity(string url, string body)
112113
// NOTE: by integrating the latest version of the WAF, blocking was disabled, as it does not support blocking yet
113114
var sanitisedUrl = VerifyHelper.SanitisePathsForVerify(url);
114115
var settings = VerifyHelper.GetSpanVerifierSettings(sanitisedUrl, body);
116+
FilterConnectionHeader(settings);
115117
return TestAppSecRequestWithVerifyAsync(_iisFixture.Agent, url, body, 5, 1, settings, "application/x-www-form-urlencoded");
116118
}
117119

tracer/test/Datadog.Trace.Security.IntegrationTests/Rcm/AspNetMvc5AsmBlockingActions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ public async Task TestBlockingAction(string type, int statusCode)
8989
var url = $"/health?arg=dummy_rule";
9090
var agent = _iisFixture.Agent;
9191
var settings = VerifyHelper.GetSpanVerifierSettings(type, statusCode);
92+
FilterConnectionHeader(settings);
9293
var fileId = nameof(TestBlockingAction);
9394
// need to reset if the process is going to be reused
9495
await agent.SetupRcmAndWait(Output, []);

tracer/test/Datadog.Trace.Security.IntegrationTests/Rcm/AspNetMvc5AsmRulesToggle.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ public async Task TestGlobalRulesToggling()
8787
var url = $"/health";
8888
var agent = _iisFixture.Agent;
8989
var settings = VerifyHelper.GetSpanVerifierSettings();
90+
FilterConnectionHeader(settings);
9091
var fileId = nameof(TestGlobalRulesToggling);
9192
var userAgent = "(sql power injector)";
9293

tracer/test/Datadog.Trace.TestHelpers.AutoInstrumentation/IisFixture.cs

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
using System.Diagnostics;
88
using System.IO;
99
using System.Net;
10+
using System.Net.Http;
11+
using System.Net.Sockets;
1012
using System.Threading.Tasks;
1113
using Xunit;
1214

@@ -27,7 +29,7 @@ public sealed class IisFixture : GacFixture, IDisposable
2729

2830
public bool UseGac { get; set; } = true;
2931

30-
public async Task TryStartIis(TestHelper helper, IisAppType appType)
32+
public async Task TryStartIis(TestHelper helper, IisAppType appType, bool sendHealthCheck = true, string url = "")
3133
{
3234
if (IisExpress.Process == null)
3335
{
@@ -41,6 +43,8 @@ public async Task TryStartIis(TestHelper helper, IisAppType appType)
4143

4244
HttpPort = TcpPortProvider.GetOpenPort();
4345
IisExpress = await helper.StartIISExpress(Agent, HttpPort, appType, VirtualApplicationPath);
46+
47+
await EnsureServerStarted(sendHealthCheck, url);
4448
}
4549
}
4650

@@ -95,5 +99,84 @@ public void Dispose()
9599
}
96100
}
97101
}
102+
103+
private async Task EnsureServerStarted(bool sendHealthCheck, string url)
104+
{
105+
var maxMillisecondsToWait = 30_000;
106+
var intervalMilliseconds = 500;
107+
var intervals = maxMillisecondsToWait / intervalMilliseconds;
108+
var serverReady = false;
109+
110+
// if we end up retrying, accept spans from previous attempts
111+
var dateTime = DateTime.UtcNow;
112+
113+
// wait for server to be ready to receive requests
114+
while (intervals-- > 0)
115+
{
116+
DateTime startTime = DateTime.Now;
117+
try
118+
{
119+
if (sendHealthCheck)
120+
{
121+
var request = WebRequest.CreateHttp($"http://localhost:{HttpPort}{url}");
122+
var response = request.GetResponse();
123+
var responseCode = ((HttpWebResponse)response).StatusCode;
124+
response.Close();
125+
126+
if (responseCode == HttpStatusCode.OK)
127+
{
128+
await Agent.WaitForSpansAsync(1, minDateTime: dateTime);
129+
serverReady = true;
130+
}
131+
}
132+
else
133+
{
134+
serverReady = await IsPortListeningAsync(HttpPort);
135+
}
136+
}
137+
catch
138+
{
139+
// ignore
140+
}
141+
142+
if (serverReady)
143+
{
144+
break;
145+
}
146+
147+
var milisecondsElapsed = (DateTime.Now - startTime).TotalMilliseconds;
148+
149+
if (milisecondsElapsed < intervalMilliseconds)
150+
{
151+
await Task.Delay((int)(intervalMilliseconds - milisecondsElapsed));
152+
}
153+
}
154+
155+
if (!serverReady)
156+
{
157+
throw new Exception("Couldn't verify the application is ready to receive requests.");
158+
}
159+
}
160+
161+
private async Task<bool> IsPortListeningAsync(int port)
162+
{
163+
try
164+
{
165+
using (var client = new TcpClient())
166+
{
167+
var task = client.ConnectAsync("127.0.0.1", port);
168+
if (await Task.WhenAny(task, Task.Delay(1000)) == task)
169+
{
170+
return client.Connected;
171+
}
172+
}
173+
}
174+
catch
175+
{
176+
// If there's an exception, the server is not listening on this port
177+
}
178+
179+
return false;
180+
}
98181
}
99182
}

tracer/test/Datadog.Trace.Tools.dd_dotnet.ArtifactTests/Checks/IisCheckTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ private async Task<IisFixture> StartIis(IisAppType appType)
230230

231231
try
232232
{
233-
await fixture.TryStartIis(this, appType);
233+
await fixture.TryStartIis(this, appType, false);
234234
}
235235
catch (Exception)
236236
{

tracer/test/Datadog.Trace.Tools.dd_dotnet.IntegrationTests/Checks/IisCheckTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ private async Task<IisFixture> StartIis(IisAppType appType, bool sendRequest = t
286286

287287
try
288288
{
289-
await fixture.TryStartIis(this, appType);
289+
await fixture.TryStartIis(this, appType, false);
290290
}
291291
catch (Exception)
292292
{

tracer/test/snapshots/Security.AspNetMvc5AsmBlockingActions.Classic.enableSecurity=True.__type=block_request_statusCode=200.verified.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
runtime-id: Guid_1,
2525
span.kind: server,
2626
_dd.appsec.fp.http.endpoint: http-get-0587c50e-b25f03de-,
27-
_dd.appsec.fp.http.header: hdr-0000000000-3626b5f8-1-4740ae63,
27+
_dd.appsec.fp.http.header: hdr-0X00000000-3626b5f8-1-4740ae63,
2828
_dd.appsec.fp.http.network: net-1-1000000000,
2929
_dd.appsec.json: {"triggers":[{"rule":{"id":"test-dummy-rule","name":"Dummy rule to test blocking","tags":{"category":"attack_attempt","type":"security_scanner"}},"rule_matches":[{"operator":"phrase_match","operator_value":"","parameters":[{"address":"server.request.query","highlight":["dummy_rule"],"key_path":["arg","0"],"value":"dummy_rule"}]}]}]},
3030
_dd.origin: appsec,
@@ -68,7 +68,7 @@
6868
runtime-id: Guid_1,
6969
span.kind: server,
7070
_dd.appsec.fp.http.endpoint: http-get-0587c50e-b25f03de-,
71-
_dd.appsec.fp.http.header: hdr-0000000000-3626b5f8-1-4740ae63,
71+
_dd.appsec.fp.http.header: hdr-0X00000000-3626b5f8-1-4740ae63,
7272
_dd.appsec.fp.http.network: net-1-1000000000,
7373
_dd.appsec.json: {"triggers":[{"rule":{"id":"test-dummy-rule","name":"Dummy rule to test blocking","tags":{"category":"attack_attempt","type":"security_scanner"}},"rule_matches":[{"operator":"phrase_match","operator_value":"","parameters":[{"address":"server.request.query","highlight":["dummy_rule"],"key_path":["arg","0"],"value":"dummy_rule"}]}]}]},
7474
_dd.origin: appsec,

tracer/test/snapshots/Security.AspNetMvc5AsmBlockingActions.Classic.enableSecurity=True.__type=redirect_request_statusCode=302.verified.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
runtime-id: Guid_1,
2525
span.kind: server,
2626
_dd.appsec.fp.http.endpoint: http-get-0587c50e-b25f03de-,
27-
_dd.appsec.fp.http.header: hdr-0100000000-3626b5f8-1-4740ae63,
27+
_dd.appsec.fp.http.header: hdr-0X00000000-3626b5f8-1-4740ae63,
2828
_dd.appsec.fp.http.network: net-1-1000000000,
2929
_dd.appsec.json: {"triggers":[{"rule":{"id":"test-dummy-rule","name":"Dummy rule to test blocking","tags":{"category":"attack_attempt","type":"security_scanner"}},"rule_matches":[{"operator":"phrase_match","operator_value":"","parameters":[{"address":"server.request.query","highlight":["dummy_rule"],"key_path":["arg","0"],"value":"dummy_rule"}]}]}]},
3030
_dd.origin: appsec,
@@ -68,7 +68,7 @@
6868
runtime-id: Guid_1,
6969
span.kind: server,
7070
_dd.appsec.fp.http.endpoint: http-get-0587c50e-b25f03de-,
71-
_dd.appsec.fp.http.header: hdr-0000000000-3626b5f8-1-4740ae63,
71+
_dd.appsec.fp.http.header: hdr-0X00000000-3626b5f8-1-4740ae63,
7272
_dd.appsec.fp.http.network: net-1-1000000000,
7373
_dd.appsec.json: {"triggers":[{"rule":{"id":"test-dummy-rule","name":"Dummy rule to test blocking","tags":{"category":"attack_attempt","type":"security_scanner"}},"rule_matches":[{"operator":"phrase_match","operator_value":"","parameters":[{"address":"server.request.query","highlight":["dummy_rule"],"key_path":["arg","0"],"value":"dummy_rule"}]}]}]},
7474
_dd.origin: appsec,

tracer/test/snapshots/Security.AspNetMvc5AsmBlockingActions.Integrated.enableSecurity=True.__type=block_request_statusCode=200.verified.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
runtime-id: Guid_1,
2626
span.kind: server,
2727
_dd.appsec.fp.http.endpoint: http-get-0587c50e-b25f03de-,
28-
_dd.appsec.fp.http.header: hdr-0100000000-3626b5f8-3-98425651,
28+
_dd.appsec.fp.http.header: hdr-0X00000000-3626b5f8-3-98425651,
2929
_dd.appsec.fp.http.network: net-1-1000000000,
3030
_dd.appsec.json: {"triggers":[{"rule":{"id":"test-dummy-rule","name":"Dummy rule to test blocking","tags":{"category":"attack_attempt","type":"security_scanner"}},"rule_matches":[{"operator":"phrase_match","operator_value":"","parameters":[{"address":"server.request.query","highlight":["dummy_rule"],"key_path":["arg","0"],"value":"dummy_rule"}]}]}]},
3131
_dd.origin: appsec,
@@ -70,7 +70,7 @@
7070
runtime-id: Guid_1,
7171
span.kind: server,
7272
_dd.appsec.fp.http.endpoint: http-get-0587c50e-b25f03de-,
73-
_dd.appsec.fp.http.header: hdr-0000000000-3626b5f8-3-98425651,
73+
_dd.appsec.fp.http.header: hdr-0X00000000-3626b5f8-3-98425651,
7474
_dd.appsec.fp.http.network: net-1-1000000000,
7575
_dd.appsec.json: {"triggers":[{"rule":{"id":"test-dummy-rule","name":"Dummy rule to test blocking","tags":{"category":"attack_attempt","type":"security_scanner"}},"rule_matches":[{"operator":"phrase_match","operator_value":"","parameters":[{"address":"server.request.query","highlight":["dummy_rule"],"key_path":["arg","0"],"value":"dummy_rule"}]}]}]},
7676
_dd.origin: appsec,

0 commit comments

Comments
 (0)