11// Copyright The OpenTelemetry Authors
22// SPDX-License-Identifier: Apache-2.0
33
4+ using System . Runtime . CompilerServices ;
5+ using System . Text ;
46using System . Web ;
57using System . Web . Routing ;
68
@@ -48,6 +50,20 @@ internal sealed class HttpRequestRouteHelper
4850 return template ;
4951 }
5052
53+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
54+ private static bool CompareStringToSubstring ( string example , string target , int start )
55+ {
56+ for ( int i = 0 ; i < example . Length ; i ++ )
57+ {
58+ if ( target [ start + 1 + i ] != example [ i ] )
59+ {
60+ return false ;
61+ }
62+ }
63+
64+ return true ;
65+ }
66+
5167 private static string PrepareRouteTemplate ( Route route , RouteData routeData )
5268 {
5369 const string controllerToken = "controller" ;
@@ -56,32 +72,58 @@ private static string PrepareRouteTemplate(Route route, RouteData routeData)
5672 var template = route . Url ;
5773 var controller = ( string ) routeData . Values [ controllerToken ] ;
5874 var action = ( string ) routeData . Values [ actionToken ] ;
75+ var hasController = ! string . IsNullOrWhiteSpace ( controller ) ;
76+ var hasAction = ! string . IsNullOrWhiteSpace ( action ) ;
77+ var sb = new StringBuilder ( template . Length ) ;
5978
60- if ( ! string . IsNullOrWhiteSpace ( controller ) )
79+ int i = 0 ;
80+ while ( i < template . Length )
6181 {
62- template = template . Replace ( $ "{{{controllerToken}}}", controller ) ;
63- }
82+ if ( template [ i ] == '{' )
83+ {
84+ int end = template . IndexOf ( '}' , i + 1 ) ;
85+ if ( end != - 1 )
86+ {
87+ if ( hasController && CompareStringToSubstring ( controllerToken , template , i ) )
88+ {
89+ sb . Append ( controller ) ;
90+ }
91+ else if ( hasAction && CompareStringToSubstring ( actionToken , template , i ) )
92+ {
93+ sb . Append ( action ) ;
94+ }
95+ else
96+ {
97+ var defaults = route . Defaults ;
98+ var values = routeData . Values ;
99+ var token = template . Substring ( i + 1 , end - i - 1 ) ;
100+
101+ if ( defaults . ContainsKey ( token ) && ! values . ContainsKey ( token ) )
102+ {
103+ // Ignore defaults with no values.
104+ }
105+ else
106+ {
107+ sb . Append ( '{' ) . Append ( token ) . Append ( '}' ) ;
108+ }
109+ }
110+
111+ i = end + 1 ;
112+ continue ;
113+ }
114+ }
64115
65- if ( ! string . IsNullOrWhiteSpace ( action ) )
66- {
67- template = template . Replace ( $ "{{{actionToken}}}", action ) ;
116+ sb . Append ( template [ i ] ) ;
117+ i ++ ;
68118 }
69119
70- // Remove defaults with no values.
71- var defaultKeys = route . Defaults . Keys ;
72- var valueKeys = routeData . Values . Keys ;
73-
74- foreach ( var token in defaultKeys )
120+ // Normalizes endings by removing trailing slashes.
121+ int len = sb . Length ;
122+ while ( len > 0 && sb [ len - 1 ] == '/' )
75123 {
76- if ( valueKeys . Contains ( token ) )
77- {
78- continue ;
79- }
80-
81- template = template . Replace ( $ "{{{token}}}", string . Empty ) ;
124+ len -- ;
82125 }
83126
84- return template
85- . TrimEnd ( '/' ) ; // Normalizes endings
127+ return sb . ToString ( 0 , len ) ;
86128 }
87129}
0 commit comments