public async Task TestBasicReceiveAndResponseEventsWitPassThroughSampling(string method) { using var eventRecords = new ActivitySourceRecorder(activityDataRequest: ActivityDataRequest.PropagationData); // Send a random Http request to generate some events using (var client = new HttpClient()) { (method == "GET" ? await client.GetAsync(this.BuildRequestUrl()) : await client.PostAsync(this.BuildRequestUrl(), new StringContent("hello world"))).Dispose(); } // We should have exactly one Start and one Stop event Assert.Equal(2, eventRecords.Records.Count); Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key == "Start")); Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key == "Stop")); // Check to make sure: The first record must be a request, the next record must be a response. (Activity activity, HttpWebRequest startRequest) = AssertFirstEventWasStart(eventRecords); VerifyHeaders(startRequest); Assert.True(eventRecords.Records.TryDequeue(out var stopEvent)); Assert.Equal("Stop", stopEvent.Key); HttpWebResponse response = (HttpWebResponse)stopEvent.Value.GetCustomProperty("HttpWebRequest.Response"); Assert.NotNull(response); Assert.Empty(activity.Tags); }
public async Task TestBasicReceiveAndResponseEvents(string method, string queryString = null) { var url = this.BuildRequestUrl(queryString: queryString); using var eventRecords = new ActivitySourceRecorder(); // Send a random Http request to generate some events using (var client = new HttpClient()) { (method == "GET" ? await client.GetAsync(url) : await client.PostAsync(url, new StringContent("hello world"))).Dispose(); } // We should have exactly one Start and one Stop event Assert.Equal(2, eventRecords.Records.Count); Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key == "Start")); Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key == "Stop")); // Check to make sure: The first record must be a request, the next record must be a response. (Activity activity, HttpWebRequest startRequest) = AssertFirstEventWasStart(eventRecords); VerifyHeaders(startRequest); VerifyActivityStartTags(this.hostNameAndPort, method, url, activity); Assert.True(eventRecords.Records.TryDequeue(out var stopEvent)); Assert.Equal("Stop", stopEvent.Key); HttpWebResponse response = (HttpWebResponse)stopEvent.Value.GetCustomProperty("HttpWebRequest.Response"); Assert.NotNull(response); VerifyActivityStopTags(200, "OK", activity); }
public async Task TestInvalidBaggage() { Baggage .SetBaggage("key", "value") .SetBaggage("bad/key", "value") .SetBaggage("goodkey", "bad/value"); using var eventRecords = new ActivitySourceRecorder(); using (var client = new HttpClient()) { (await client.GetAsync(this.BuildRequestUrl())).Dispose(); } Assert.Equal(2, eventRecords.Records.Count()); Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key == "Start")); Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key == "Stop")); WebRequest thisRequest = (WebRequest)eventRecords.Records.First().Value.GetCustomProperty(HttpWebRequestActivitySource.RequestCustomPropertyName); string[] baggage = thisRequest.Headers["Baggage"].Split(','); Assert.Equal(3, baggage.Length); Assert.Contains("key=value", baggage); Assert.Contains("bad%2Fkey=value", baggage); Assert.Contains("goodkey=bad%2Fvalue", baggage); }
public async Task TestSecureTransportFailureRequest(string method) { string url = "https://expired.badssl.com/"; using var eventRecords = new ActivitySourceRecorder(); using (var client = new HttpClient()) { var ex = await Assert.ThrowsAnyAsync <Exception>(() => { // https://expired.badssl.com/ has an expired certificae. return(method == "GET" ? client.GetAsync(url) : client.PostAsync(url, new StringContent("hello world"))); }); Assert.True(ex is HttpRequestException); } // We should have one Start event and one Stop event with an exception. Assert.Equal(2, eventRecords.Records.Count()); Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key == "Start")); Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key == "Stop")); (Activity activity, HttpWebRequest startRequest) = AssertFirstEventWasStart(eventRecords); VerifyHeaders(startRequest); VerifyActivityStartTags(null, method, url, activity); Assert.True(eventRecords.Records.TryDequeue(out KeyValuePair <string, Activity> exceptionEvent)); Assert.Equal("Stop", exceptionEvent.Key); Exception exceptionException = (Exception)exceptionEvent.Value.GetCustomProperty("HttpWebRequest.Exception"); Assert.Contains(exceptionEvent.Value.Tags, i => i.Key == SpanAttributeConstants.StatusCodeKey); Assert.Contains(exceptionEvent.Value.Tags, i => i.Key == SpanAttributeConstants.StatusDescriptionKey); }
public async Task TestInvalidBaggage() { var parentActivity = new Activity("parent") .AddBaggage("key", "value") .AddBaggage("bad/key", "value") .AddBaggage("goodkey", "bad/value") .Start(); using (var eventRecords = new ActivitySourceRecorder()) { using (var client = new HttpClient()) { (await client.GetAsync(this.BuildRequestUrl())).Dispose(); } Assert.Equal(2, eventRecords.Records.Count()); Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key == "Start")); Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key == "Stop")); WebRequest thisRequest = (WebRequest)eventRecords.Records.First().Value.GetCustomProperty("HttpWebRequest.Request"); string[] correlationContext = thisRequest.Headers["Correlation-Context"].Split(','); Assert.Equal(3, correlationContext.Length); Assert.Contains("key=value", correlationContext); Assert.Contains("bad%2Fkey=value", correlationContext); Assert.Contains("goodkey=bad%2Fvalue", correlationContext); } parentActivity.Stop(); }
public async Task TestCanceledRequest(string method) { string url = this.BuildRequestUrl(); CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); using var eventRecords = new ActivitySourceRecorder(_ => { cts.Cancel(); }); using (var client = new HttpClient()) { var ex = await Assert.ThrowsAnyAsync <Exception>(() => { return(method == "GET" ? client.GetAsync(url, cts.Token) : client.PostAsync(url, new StringContent("hello world"), cts.Token)); }); Assert.True(ex is TaskCanceledException || ex is WebException); } // We should have one Start event and one Stop event with an exception. Assert.Equal(2, eventRecords.Records.Count()); Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key == "Start")); Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key == "Stop")); Activity activity = AssertFirstEventWasStart(eventRecords); VerifyActivityStartTags(this.hostNameAndPort, method, url, activity); Assert.True(eventRecords.Records.TryDequeue(out KeyValuePair <string, Activity> exceptionEvent)); Assert.Equal("Stop", exceptionEvent.Key); Assert.NotNull(exceptionEvent.Value.GetTagValue(SpanAttributeConstants.StatusCodeKey)); Assert.Null(exceptionEvent.Value.GetTagValue(SpanAttributeConstants.StatusDescriptionKey)); }
public async Task TestResponseWithoutContentEvents(string method) { string url = this.BuildRequestUrl(queryString: "responseCode=204"); using var eventRecords = new ActivitySourceRecorder(); // Send a random Http request to generate some events using (var client = new HttpClient()) { using HttpResponseMessage response = method == "GET" ? await client.GetAsync(url) : await client.PostAsync(url, new StringContent ("hello world")); } // We should have exactly one Start and one Stop event Assert.Equal(2, eventRecords.Records.Count); Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key == "Start")); Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key == "Stop")); // Check to make sure: The first record must be a request, the next record must be a response. Activity activity = AssertFirstEventWasStart(eventRecords); VerifyActivityStartTags(this.hostNameAndPort, method, url, activity); Assert.True(eventRecords.Records.TryDequeue(out var stopEvent)); Assert.Equal("Stop", stopEvent.Key); VerifyActivityStopTags(204, activity); }
public async Task TestTraceStateAndBaggage() { try { using var eventRecords = new ActivitySourceRecorder(); var parent = new Activity("w3c activity"); parent.SetParentId(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom()); parent.TraceStateString = "some=state"; parent.Start(); Baggage.SetBaggage("k", "v"); // Send a random Http request to generate some events using (var client = new HttpClient()) { (await client.GetAsync(this.BuildRequestUrl())).Dispose(); } parent.Stop(); Assert.Equal(2, eventRecords.Records.Count()); // Check to make sure: The first record must be a request, the next record must be a response. _ = AssertFirstEventWasStart(eventRecords); } finally { this.CleanUpActivity(); } }
public async Task DoNotInjectTraceParentWhenPresent(string method) { try { using var eventRecords = new ActivitySourceRecorder(); // Send a random Http request to generate some events using (var client = new HttpClient()) using (var request = new HttpRequestMessage(HttpMethod.Get, this.BuildRequestUrl())) { request.Headers.Add("traceparent", "00-abcdef0123456789abcdef0123456789-abcdef0123456789-01"); if (method == "GET") { request.Method = HttpMethod.Get; } else { request.Method = HttpMethod.Post; request.Content = new StringContent("hello world"); } (await client.SendAsync(request)).Dispose(); } // No events are sent. Assert.Empty(eventRecords.Records); } finally { this.CleanUpActivity(); } }
private static (Activity, HttpWebRequest) AssertFirstEventWasStart(ActivitySourceRecorder eventRecords) { Assert.True(eventRecords.Records.TryDequeue(out KeyValuePair <string, Activity> startEvent)); Assert.Equal("Start", startEvent.Key); HttpWebRequest startRequest = (HttpWebRequest)startEvent.Value.GetCustomProperty("HttpWebRequest.Request"); Assert.NotNull(startRequest); return(startEvent.Value, startRequest); }
public void TestMultipleConcurrentRequests() { ServicePointManager.DefaultConnectionLimit = int.MaxValue; var parentActivity = new Activity("parent").Start(); using var eventRecords = new ActivitySourceRecorder(); Dictionary <Uri, Tuple <WebRequest, WebResponse> > requestData = new Dictionary <Uri, Tuple <WebRequest, WebResponse> >(); for (int i = 0; i < 10; i++) { Uri uriWithRedirect = new Uri(this.BuildRequestUrl(queryString: $"q={i}&redirects=3")); requestData[uriWithRedirect] = null; } // Issue all requests simultaneously HttpClient httpClient = new HttpClient(); Dictionary <Uri, Task <HttpResponseMessage> > tasks = new Dictionary <Uri, Task <HttpResponseMessage> >(); CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); foreach (var url in requestData.Keys) { tasks.Add(url, httpClient.GetAsync(url, cts.Token)); } // wait up to 10 sec for all requests and suppress exceptions Task.WhenAll(tasks.Select(t => t.Value).ToArray()).ContinueWith(tt => { foreach (var task in tasks) { task.Value.Result?.Dispose(); } }).Wait(); // Examine the result. Make sure we got all successful requests. // Just make sure some events are written, to confirm we successfully subscribed to it. We should have // exactly 1 Start event per request and exactly 1 Stop event per response (if request succeeded) var successfulTasks = tasks.Where(t => t.Value.Status == TaskStatus.RanToCompletion); Assert.Equal(tasks.Count, eventRecords.Records.Count(rec => rec.Key == "Start")); Assert.Equal(successfulTasks.Count(), eventRecords.Records.Count(rec => rec.Key == "Stop")); // Check to make sure: We have a WebRequest and a WebResponse for each successful request foreach (var pair in eventRecords.Records) { Activity activity = pair.Value; Assert.True( pair.Key == "Start" || pair.Key == "Stop", "An unexpected event of name " + pair.Key + "was received"); } }
public async Task TestReflectInitializationViaSubscription() { using var eventRecords = new ActivitySourceRecorder(); // Send a random Http request to generate some events using (var client = new HttpClient()) { (await client.GetAsync(this.BuildRequestUrl())).Dispose(); } // Just make sure some events are written, to confirm we successfully subscribed to it. // We should have exactly one Start and one Stop event Assert.Equal(2, eventRecords.Records.Count); }
public async Task TestBasicReceiveAndResponseEventsWithoutSampling(string method) { using var eventRecords = new ActivitySourceRecorder(activityDataRequest: ActivityDataRequest.None); // Send a random Http request to generate some events using (var client = new HttpClient()) { (method == "GET" ? await client.GetAsync(this.BuildRequestUrl()) : await client.PostAsync(this.BuildRequestUrl(), new StringContent("hello world"))).Dispose(); } // There should be no events because we turned off sampling. Assert.Empty(eventRecords.Records); }
public async Task TestRedirectedRequest(string method) { using var eventRecords = new ActivitySourceRecorder(); using (var client = new HttpClient()) { using HttpResponseMessage response = method == "GET" ? await client.GetAsync(this.BuildRequestUrl (queryString : "redirects=10")) : await client.PostAsync(this.BuildRequestUrl (queryString : "redirects=10"), new StringContent ("hello world")); } // We should have exactly one Start and one Stop event Assert.Equal(2, eventRecords.Records.Count()); Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key == "Start")); Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key == "Stop")); }
public async Task TestRequestWithException(string method) { string url = method == "GET" ? $"http://{Guid.NewGuid()}.com" : $"http://{Guid.NewGuid()}.com"; using var eventRecords = new ActivitySourceRecorder(); var ex = await Assert.ThrowsAsync <HttpRequestException>(() => { return(method == "GET" ? new HttpClient().GetAsync(url) : new HttpClient().PostAsync(url, new StringContent("hello world"))); }); // check that request failed because of the wrong domain name and not because of reflection var webException = (WebException)ex.InnerException; Assert.NotNull(webException); Assert.True(webException.Status == WebExceptionStatus.NameResolutionFailure); // We should have one Start event and one Stop event with an exception. Assert.Equal(2, eventRecords.Records.Count()); Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key == "Start")); Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key == "Stop")); // Check to make sure: The first record must be a request, the next record must be an exception. (Activity activity, HttpWebRequest startRequest) = AssertFirstEventWasStart(eventRecords); VerifyHeaders(startRequest); VerifyActivityStartTags(null, method, url, activity); Assert.True(eventRecords.Records.TryDequeue(out KeyValuePair <string, Activity> exceptionEvent)); Assert.Equal("Stop", exceptionEvent.Key); HttpWebRequest exceptionRequest = (HttpWebRequest)exceptionEvent.Value.GetCustomProperty("HttpWebRequest.Request"); Assert.Equal(startRequest, exceptionRequest); Exception exceptionException = (Exception)exceptionEvent.Value.GetCustomProperty("HttpWebRequest.Exception"); Assert.Equal(webException, exceptionException); Assert.Contains(activity.Tags, i => i.Key == SpanAttributeConstants.StatusCodeKey); Assert.Contains(activity.Tags, i => i.Key == SpanAttributeConstants.StatusDescriptionKey); }
public async Task TestInvalidBaggage() { validateBaggage = true; Baggage .SetBaggage("key", "value") .SetBaggage("bad/key", "value") .SetBaggage("goodkey", "bad/value"); using var eventRecords = new ActivitySourceRecorder(); using (var client = new HttpClient()) { (await client.GetAsync(this.BuildRequestUrl())).Dispose(); } Assert.Equal(2, eventRecords.Records.Count()); Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key == "Start")); Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key == "Stop")); validateBaggage = false; }
public async Task TestTraceStateAndBaggage() { try { using var eventRecords = new ActivitySourceRecorder(); var parent = new Activity("w3c activity"); parent.SetParentId(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom()); parent.TraceStateString = "some=state"; parent.Start(); Baggage.SetBaggage("k", "v"); // Send a random Http request to generate some events using (var client = new HttpClient()) { (await client.GetAsync(this.BuildRequestUrl())).Dispose(); } parent.Stop(); Assert.Equal(2, eventRecords.Records.Count()); // Check to make sure: The first record must be a request, the next record must be a response. (Activity activity, HttpWebRequest startRequest) = AssertFirstEventWasStart(eventRecords); var traceparent = startRequest.Headers["traceparent"]; var tracestate = startRequest.Headers["tracestate"]; var baggage = startRequest.Headers["baggage"]; Assert.NotNull(traceparent); Assert.Equal("some=state", tracestate); Assert.Equal("k=v", baggage); Assert.StartsWith($"00-{parent.TraceId.ToHexString()}-", traceparent); Assert.Matches("^[0-9a-f]{2}-[0-9a-f]{32}-[0-9a-f]{16}-[0-9a-f]{2}$", traceparent); } finally { this.CleanUpActivity(); } }
public async Task TestSecureTransportRetryFailureRequest(string method) { // This test sends an https request to an endpoint only set up for http. // It should retry. What we want to test for is 1 start, 1 exception event even // though multiple are actually sent. string url = this.BuildRequestUrl(useHttps: true); using var eventRecords = new ActivitySourceRecorder(); using (var client = new HttpClient()) { var ex = await Assert.ThrowsAnyAsync <Exception>(() => { return(method == "GET" ? client.GetAsync(url) : client.PostAsync(url, new StringContent("hello world"))); }); Assert.True(ex is HttpRequestException); } // We should have one Start event and one Stop event with an exception. Assert.Equal(2, eventRecords.Records.Count()); Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key == "Start")); Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key == "Stop")); (Activity activity, HttpWebRequest startRequest) = AssertFirstEventWasStart(eventRecords); VerifyHeaders(startRequest); VerifyActivityStartTags(this.hostNameAndPort, method, url, activity); Assert.True(eventRecords.Records.TryDequeue(out KeyValuePair <string, Activity> exceptionEvent)); Assert.Equal("Stop", exceptionEvent.Key); Exception exceptionException = (Exception)exceptionEvent.Value.GetCustomProperty("HttpWebRequest.Exception"); Assert.Contains(exceptionEvent.Value.Tags, i => i.Key == SpanAttributeConstants.StatusCodeKey); Assert.Contains(exceptionEvent.Value.Tags, i => i.Key == SpanAttributeConstants.StatusDescriptionKey); }
public void TestMultipleConcurrentRequests() { ServicePointManager.DefaultConnectionLimit = int.MaxValue; var parentActivity = new Activity("parent").Start(); using var eventRecords = new ActivitySourceRecorder(); Dictionary <Uri, Tuple <WebRequest, WebResponse> > requestData = new Dictionary <Uri, Tuple <WebRequest, WebResponse> >(); for (int i = 0; i < 10; i++) { Uri uriWithRedirect = new Uri(this.BuildRequestUrl(queryString: $"q={i}&redirects=3")); requestData[uriWithRedirect] = null; } // Issue all requests simultaneously HttpClient httpClient = new HttpClient(); Dictionary <Uri, Task <HttpResponseMessage> > tasks = new Dictionary <Uri, Task <HttpResponseMessage> >(); CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); foreach (var url in requestData.Keys) { tasks.Add(url, httpClient.GetAsync(url, cts.Token)); } // wait up to 10 sec for all requests and suppress exceptions Task.WhenAll(tasks.Select(t => t.Value).ToArray()).ContinueWith(tt => { foreach (var task in tasks) { task.Value.Result?.Dispose(); } }).Wait(); // Examine the result. Make sure we got all successful requests. // Just make sure some events are written, to confirm we successfully subscribed to it. We should have // exactly 1 Start event per request and exactly 1 Stop event per response (if request succeeded) var successfulTasks = tasks.Where(t => t.Value.Status == TaskStatus.RanToCompletion); Assert.Equal(tasks.Count, eventRecords.Records.Count(rec => rec.Key == "Start")); Assert.Equal(successfulTasks.Count(), eventRecords.Records.Count(rec => rec.Key == "Stop")); // Check to make sure: We have a WebRequest and a WebResponse for each successful request foreach (var pair in eventRecords.Records) { Activity activity = pair.Value; Assert.True( pair.Key == "Start" || pair.Key == "Stop", "An unexpected event of name " + pair.Key + "was received"); WebRequest request = (WebRequest)activity.GetCustomProperty("HttpWebRequest.Request"); Assert.Equal("HttpWebRequest", request.GetType().Name); if (pair.Key == "Start") { // Make sure this is an URL that we recognize. If not, just skip if (!requestData.TryGetValue(request.RequestUri, out var tuple)) { continue; } // all requests have traceparent with proper parent Id var traceparent = request.Headers["traceparent"]; Assert.StartsWith($"00-{parentActivity.TraceId.ToHexString()}-", traceparent); Assert.Null(requestData[request.RequestUri]); requestData[request.RequestUri] = new Tuple <WebRequest, WebResponse>(request, null); } else { // This must be the response. WebResponse response = (WebResponse)activity.GetCustomProperty("HttpWebRequest.Response"); Assert.Equal("HttpWebResponse", response.GetType().Name); // By the time we see the response, the request object may already have been redirected with a different // url. Hence, it's not reliable to just look up requestData by the URL/hostname. Instead, we have to look // through each one and match by object reference on the request object. Tuple <WebRequest, WebResponse> tuple = null; foreach (Tuple <WebRequest, WebResponse> currentTuple in requestData.Values) { if (currentTuple != null && currentTuple.Item1 == request) { // Found it! tuple = currentTuple; break; } } // Update the tuple with the response object Assert.NotNull(tuple); requestData[request.RequestUri] = new Tuple <WebRequest, WebResponse>(request, response); } } // Finally, make sure we have request and response objects for every successful request foreach (KeyValuePair <Uri, Tuple <WebRequest, WebResponse> > pair in requestData) { if (successfulTasks.Any(t => t.Key == pair.Key)) { Assert.NotNull(pair.Value); Assert.NotNull(pair.Value.Item1); Assert.NotNull(pair.Value.Item2); } } }
private static Activity AssertFirstEventWasStart(ActivitySourceRecorder eventRecords) { Assert.True(eventRecords.Records.TryDequeue(out KeyValuePair <string, Activity> startEvent)); Assert.Equal("Start", startEvent.Key); return(startEvent.Value); }
public async Task TestBasicReceiveAndResponseWebRequestEvents(string method, int mode) { string url = this.BuildRequestUrl(); using var eventRecords = new ActivitySourceRecorder(); // Send a random Http request to generate some events var webRequest = (HttpWebRequest)WebRequest.Create(url); if (method == "POST") { webRequest.Method = method; Stream stream = null; switch (mode) { case 0: stream = webRequest.GetRequestStream(); break; case 1: stream = await webRequest.GetRequestStreamAsync(); break; case 2: { object state = new object(); using EventWaitHandle handle = new EventWaitHandle(false, EventResetMode.ManualReset); IAsyncResult asyncResult = webRequest.BeginGetRequestStream( ar => { Assert.Equal(state, ar.AsyncState); handle.Set(); }, state); stream = webRequest.EndGetRequestStream(asyncResult); if (!handle.WaitOne(TimeSpan.FromSeconds(30))) { throw new InvalidOperationException(); } handle.Dispose(); } break; case 3: { using EventWaitHandle handle = new EventWaitHandle(false, EventResetMode.ManualReset); object state = new object(); webRequest.BeginGetRequestStream( ar => { stream = webRequest.EndGetRequestStream(ar); Assert.Equal(state, ar.AsyncState); handle.Set(); }, state); if (!handle.WaitOne(TimeSpan.FromSeconds(30))) { throw new InvalidOperationException(); } handle.Dispose(); } break; default: throw new NotSupportedException(); } Assert.NotNull(stream); using StreamWriter writer = new StreamWriter(stream); writer.WriteLine("hello world"); } WebResponse webResponse = null; switch (mode) { case 0: webResponse = webRequest.GetResponse(); break; case 1: webResponse = await webRequest.GetResponseAsync(); break; case 2: { object state = new object(); using EventWaitHandle handle = new EventWaitHandle(false, EventResetMode.ManualReset); IAsyncResult asyncResult = webRequest.BeginGetResponse( ar => { Assert.Equal(state, ar.AsyncState); handle.Set(); }, state); webResponse = webRequest.EndGetResponse(asyncResult); if (!handle.WaitOne(TimeSpan.FromSeconds(30))) { throw new InvalidOperationException(); } handle.Dispose(); } break; case 3: { using EventWaitHandle handle = new EventWaitHandle(false, EventResetMode.ManualReset); object state = new object(); webRequest.BeginGetResponse( ar => { webResponse = webRequest.EndGetResponse(ar); Assert.Equal(state, ar.AsyncState); handle.Set(); }, state); if (!handle.WaitOne(TimeSpan.FromSeconds(30))) { throw new InvalidOperationException(); } handle.Dispose(); } break; default: throw new NotSupportedException(); } Assert.NotNull(webResponse); using StreamReader reader = new StreamReader(webResponse.GetResponseStream()); reader.ReadToEnd(); // Make sure response is not disposed. // We should have exactly one Start and one Stop event Assert.Equal(2, eventRecords.Records.Count); Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key == "Start")); Assert.Equal(1, eventRecords.Records.Count(rec => rec.Key == "Stop")); // Check to make sure: The first record must be a request, the next record must be a response. (Activity activity, HttpWebRequest startRequest) = AssertFirstEventWasStart(eventRecords); VerifyHeaders(startRequest); VerifyActivityStartTags(this.hostNameAndPort, method, url, activity); Assert.True(eventRecords.Records.TryDequeue(out var stopEvent)); Assert.Equal("Stop", stopEvent.Key); HttpWebResponse response = (HttpWebResponse)stopEvent.Value.GetCustomProperty("HttpWebRequest.Response"); Assert.NotNull(response); VerifyActivityStopTags(200, "OK", activity); }