55using System . Net ;
66using System . Net . Http ;
77using System . Net . Http . Headers ;
8+ using System . Net . Sockets ;
89using System . Security . Authentication ;
910using Microsoft . AspNetCore . Hosting ;
1011using Microsoft . AspNetCore . Http ;
1112using Microsoft . AspNetCore . Internal ;
13+ using Microsoft . AspNetCore . InternalTesting ;
1214using Microsoft . AspNetCore . Server . Kestrel . Core ;
1315using Microsoft . AspNetCore . Server . Kestrel . Core . Features ;
14- using Microsoft . AspNetCore . InternalTesting ;
1516using Microsoft . Extensions . DependencyInjection ;
16- using Microsoft . Extensions . Diagnostics . Metrics ;
1717using Microsoft . Extensions . Diagnostics . Metrics . Testing ;
1818using Microsoft . Extensions . Hosting ;
1919using Microsoft . Extensions . Logging ;
@@ -23,6 +23,72 @@ namespace Interop.FunctionalTests.Http2;
2323[ Collection ( nameof ( NoParallelCollection ) ) ]
2424public class Http2RequestTests : LoggedTest
2525{
26+ [ Fact ]
27+ public async Task InvalidHandshake_MetricsHasErrorType ( )
28+ {
29+ // Arrange
30+ var builder = CreateHostBuilder (
31+ c =>
32+ {
33+ return Task . CompletedTask ;
34+ } ,
35+ protocol : HttpProtocols . Http2 ,
36+ plaintext : true ) ;
37+
38+ using ( var host = builder . Build ( ) )
39+ {
40+ var meterFactory = host . Services . GetRequiredService < IMeterFactory > ( ) ;
41+
42+ // Use MeterListener for this test because we want to check that a single error.type tag is added.
43+ // MetricCollector can't be used for this because it stores tags in a dictionary and overwrites values.
44+ var measurementTcs = new TaskCompletionSource < Measurement < double > > ( ) ;
45+ var meterListener = new MeterListener ( ) ;
46+ meterListener . InstrumentPublished = ( instrument , meterListener ) =>
47+ {
48+ if ( instrument . Meter . Scope == meterFactory &&
49+ instrument . Meter . Name == "Microsoft.AspNetCore.Server.Kestrel" &&
50+ instrument . Name == "kestrel.connection.duration" )
51+ {
52+ meterListener . EnableMeasurementEvents ( instrument ) ;
53+ meterListener . SetMeasurementEventCallback < double > ( ( Instrument instrument , double measurement , ReadOnlySpan < KeyValuePair < string , object > > tags , object state ) =>
54+ {
55+ measurementTcs . SetResult ( new Measurement < double > ( measurement , tags ) ) ;
56+ } ) ;
57+ }
58+ } ;
59+ meterListener . Start ( ) ;
60+
61+ await host . StartAsync ( ) ;
62+ var client = HttpHelpers . CreateClient ( maxResponseHeadersLength : 1024 ) ;
63+
64+ // Act
65+ using var socket = new Socket ( SocketType . Stream , ProtocolType . Tcp ) ;
66+ socket . LingerState = new LingerOption ( false , 0 ) ;
67+
68+ socket . Connect ( IPAddress . Loopback , host . GetPort ( ) ) ;
69+ socket . Send ( new byte [ 1024 * 16 ] ) ;
70+
71+ // Wait for measurement to be available.
72+ var measurement = await measurementTcs . Task . DefaultTimeout ( ) ;
73+
74+ // Assert
75+ Assert . True ( measurement . Value > 0 ) ;
76+
77+ var tags = measurement . Tags . ToArray ( ) ;
78+ Assert . Equal ( "http" , ( string ) tags . Single ( t => t . Key == "network.protocol.name" ) . Value ) ;
79+ Assert . Equal ( "2" , ( string ) tags . Single ( t => t . Key == "network.protocol.version" ) . Value ) ;
80+ Assert . Equal ( "tcp" , ( string ) tags . Single ( t => t . Key == "network.transport" ) . Value ) ;
81+ Assert . Equal ( "ipv4" , ( string ) tags . Single ( t => t . Key == "network.type" ) . Value ) ;
82+ Assert . Equal ( "127.0.0.1" , ( string ) tags . Single ( t => t . Key == "server.address" ) . Value ) ;
83+ Assert . Equal ( host . GetPort ( ) , ( int ) tags . Single ( t => t . Key == "server.port" ) . Value ) ;
84+ Assert . Equal ( "invalid_handshake" , ( string ) tags . Single ( t => t . Key == "error.type" ) . Value ) ;
85+
86+ socket . Close ( ) ;
87+
88+ await host . StopAsync ( ) ;
89+ }
90+ }
91+
2692 [ Fact ]
2793 public async Task GET_Metrics_HttpProtocolAndTlsSet ( )
2894 {
0 commit comments