@@ -354,6 +354,49 @@ public Task RequestDuration_CustomTags_Recorded()
354
354
VerifyRequestDuration ( m , uri , UseVersion , 200 ) ;
355
355
Assert . Equal ( "/test" , m . Tags . ToArray ( ) . Single ( t => t . Key == "route" ) . Value ) ;
356
356
357
+ } , async server =>
358
+ {
359
+ await server . HandleRequestAsync ( ) ;
360
+ } ) ;
361
+ }
362
+
363
+ [ Fact ]
364
+ public Task RequestDuration_MultipleCallbacksPerRequest_AllCalledInOrder ( )
365
+ {
366
+ return LoopbackServerFactory . CreateClientAndServerAsync ( async uri =>
367
+ {
368
+ using HttpMessageInvoker client = CreateHttpMessageInvoker ( ) ;
369
+ using InstrumentRecorder < double > recorder = SetupInstrumentRecorder < double > ( InstrumentNames . RequestDuration ) ;
370
+ using HttpRequestMessage request = new ( HttpMethod . Get , uri ) { Version = UseVersion } ;
371
+
372
+ int lastCallback = - 1 ;
373
+
374
+ HttpMetricsEnrichmentContext . AddCallback ( request , ctx =>
375
+ {
376
+ Assert . Equal ( - 1 , lastCallback ) ;
377
+ lastCallback = 1 ;
378
+ ctx . AddCustomTag ( "custom1" , "foo" ) ;
379
+ } ) ;
380
+ HttpMetricsEnrichmentContext . AddCallback ( request , ctx =>
381
+ {
382
+ Assert . Equal ( 1 , lastCallback ) ;
383
+ lastCallback = 2 ;
384
+ ctx . AddCustomTag ( "custom2" , "bar" ) ;
385
+ } ) ;
386
+ HttpMetricsEnrichmentContext . AddCallback ( request , ctx =>
387
+ {
388
+ Assert . Equal ( 2 , lastCallback ) ;
389
+ ctx . AddCustomTag ( "custom3" , "baz" ) ;
390
+ } ) ;
391
+
392
+ using HttpResponseMessage response = await SendAsync ( client , request ) ;
393
+
394
+ Measurement < double > m = Assert . Single ( recorder . GetMeasurements ( ) ) ;
395
+ VerifyRequestDuration ( m , uri , UseVersion , 200 ) ;
396
+ Assert . Equal ( "foo" , Assert . Single ( m . Tags . ToArray ( ) , t => t . Key == "custom1" ) . Value ) ;
397
+ Assert . Equal ( "bar" , Assert . Single ( m . Tags . ToArray ( ) , t => t . Key == "custom2" ) . Value ) ;
398
+ Assert . Equal ( "baz" , Assert . Single ( m . Tags . ToArray ( ) , t => t . Key == "custom3" ) . Value ) ;
399
+
357
400
} , async server =>
358
401
{
359
402
await server . AcceptConnectionSendResponseAndCloseAsync ( ) ;
@@ -928,6 +971,96 @@ public class HttpMetricsTest_Http11_Async_HttpMessageInvoker : HttpMetricsTest_H
928
971
public HttpMetricsTest_Http11_Async_HttpMessageInvoker ( ITestOutputHelper output ) : base ( output )
929
972
{
930
973
}
974
+
975
+ [ Fact ]
976
+ public async Task RequestDuration_RequestReused_EnrichmentCallbacksAreCleared ( )
977
+ {
978
+ await LoopbackServerFactory . CreateClientAndServerAsync ( async uri =>
979
+ {
980
+ using HttpMessageInvoker client = CreateHttpMessageInvoker ( ) ;
981
+ using InstrumentRecorder < double > recorder = SetupInstrumentRecorder < double > ( InstrumentNames . RequestDuration ) ;
982
+
983
+ using HttpRequestMessage request = new ( HttpMethod . Get , uri ) ;
984
+
985
+ int firstCallbackCalls = 0 ;
986
+
987
+ HttpMetricsEnrichmentContext . AddCallback ( request , ctx =>
988
+ {
989
+ firstCallbackCalls ++ ;
990
+ ctx . AddCustomTag ( "key1" , "foo" ) ;
991
+ } ) ;
992
+
993
+ ( await SendAsync ( client , request ) ) . Dispose ( ) ;
994
+ Assert . Equal ( 1 , firstCallbackCalls ) ;
995
+
996
+ Measurement < double > m = Assert . Single ( recorder . GetMeasurements ( ) ) ;
997
+ Assert . Equal ( "key1" , Assert . Single ( m . Tags . ToArray ( ) , t => t . Value as string == "foo" ) . Key ) ;
998
+
999
+ HttpMetricsEnrichmentContext . AddCallback ( request , static ctx =>
1000
+ {
1001
+ ctx . AddCustomTag ( "key2" , "foo" ) ;
1002
+ } ) ;
1003
+
1004
+ ( await SendAsync ( client , request ) ) . Dispose ( ) ;
1005
+ Assert . Equal ( 1 , firstCallbackCalls ) ;
1006
+
1007
+ Assert . Equal ( 2 , recorder . GetMeasurements ( ) . Count ) ;
1008
+ m = recorder . GetMeasurements ( ) [ 1 ] ;
1009
+ Assert . Equal ( "key2" , Assert . Single ( m . Tags . ToArray ( ) , t => t . Value as string == "foo" ) . Key ) ;
1010
+ } , async server =>
1011
+ {
1012
+ await server . HandleRequestAsync ( ) ;
1013
+ await server . HandleRequestAsync ( ) ;
1014
+ } ) ;
1015
+ }
1016
+
1017
+ [ ConditionalFact ( typeof ( PlatformDetection ) , nameof ( PlatformDetection . IsThreadingSupported ) ) ]
1018
+ public async Task RequestDuration_ConcurrentRequestsSeeDifferentContexts ( )
1019
+ {
1020
+ await LoopbackServerFactory . CreateClientAndServerAsync ( async uri =>
1021
+ {
1022
+ using HttpMessageInvoker client = CreateHttpMessageInvoker ( ) ;
1023
+ using var _ = SetupInstrumentRecorder < double > ( InstrumentNames . RequestDuration ) ;
1024
+
1025
+ using HttpRequestMessage request1 = new ( HttpMethod . Get , uri ) ;
1026
+ using HttpRequestMessage request2 = new ( HttpMethod . Get , uri ) ;
1027
+
1028
+ HttpMetricsEnrichmentContext . AddCallback ( request1 , _ => { } ) ;
1029
+ ( await client . SendAsync ( request1 , CancellationToken . None ) ) . Dispose ( ) ;
1030
+
1031
+ HttpMetricsEnrichmentContext context1 = null ;
1032
+ HttpMetricsEnrichmentContext context2 = null ;
1033
+ CountdownEvent countdownEvent = new ( 2 ) ;
1034
+
1035
+ HttpMetricsEnrichmentContext . AddCallback ( request1 , ctx =>
1036
+ {
1037
+ context1 = ctx ;
1038
+ countdownEvent . Signal ( ) ;
1039
+ Assert . True ( countdownEvent . Wait ( TestHelper . PassingTestTimeout ) ) ;
1040
+ } ) ;
1041
+ HttpMetricsEnrichmentContext . AddCallback ( request2 , ctx =>
1042
+ {
1043
+ context2 = ctx ;
1044
+ countdownEvent . Signal ( ) ;
1045
+ Assert . True ( countdownEvent . Wait ( TestHelper . PassingTestTimeout ) ) ;
1046
+ } ) ;
1047
+
1048
+ Task < HttpResponseMessage > task1 = Task . Run ( ( ) => client . SendAsync ( request1 , CancellationToken . None ) ) ;
1049
+ Task < HttpResponseMessage > task2 = Task . Run ( ( ) => client . SendAsync ( request2 , CancellationToken . None ) ) ;
1050
+
1051
+ ( await task1 ) . Dispose ( ) ;
1052
+ ( await task2 ) . Dispose ( ) ;
1053
+
1054
+ Assert . NotSame ( context1 , context2 ) ;
1055
+ } , async server =>
1056
+ {
1057
+ await server . HandleRequestAsync ( ) ;
1058
+
1059
+ await Task . WhenAll (
1060
+ server . HandleRequestAsync ( ) ,
1061
+ server . HandleRequestAsync ( ) ) ;
1062
+ } , options : new GenericLoopbackOptions { ListenBacklog = 2 } ) ;
1063
+ }
931
1064
}
932
1065
933
1066
[ ConditionalClass ( typeof ( PlatformDetection ) , nameof ( PlatformDetection . IsNotMobile ) ) ]
0 commit comments