@@ -38,145 +38,152 @@ internal Request(RequestContext requestContext)
3838 {
3939 // TODO: Verbose log
4040 RequestContext = requestContext ;
41- _contentBoundaryType = BoundaryType . None ;
4241
43- RequestId = requestContext . RequestId ;
44- // For HTTP/2 Http.Sys assigns each request a unique connection id for use with API calls, but the RawConnectionId represents the real connection.
45- UConnectionId = requestContext . ConnectionId ;
46- RawConnectionId = requestContext . RawConnectionId ;
47- SslStatus = requestContext . SslStatus ;
42+ try
43+ {
44+ _contentBoundaryType = BoundaryType . None ;
4845
49- KnownMethod = requestContext . VerbId ;
50- Method = requestContext . GetVerb ( ) ! ;
46+ RequestId = requestContext . RequestId ;
47+ // For HTTP/2 Http.Sys assigns each request a unique connection id for use with API calls, but the RawConnectionId represents the real connection.
48+ UConnectionId = requestContext . ConnectionId ;
49+ RawConnectionId = requestContext . RawConnectionId ;
50+ SslStatus = requestContext . SslStatus ;
5151
52- RawUrl = requestContext . GetRawUrl ( ) ! ;
52+ KnownMethod = requestContext . VerbId ;
53+ Method = requestContext . GetVerb ( ) ! ;
5354
54- var cookedUrl = requestContext . GetCookedUrl ( ) ;
55- QueryString = cookedUrl . GetQueryString ( ) ?? string . Empty ;
55+ RawUrl = requestContext . GetRawUrl ( ) ! ;
5656
57- var rawUrlInBytes = requestContext . GetRawUrlInBytes ( ) ;
58- var originalPath = RequestUriBuilder . DecodeAndUnescapePath ( rawUrlInBytes ) ;
57+ var cookedUrl = requestContext . GetCookedUrl ( ) ;
58+ QueryString = cookedUrl . GetQueryString ( ) ?? string . Empty ;
5959
60- PathBase = string . Empty ;
61- Path = originalPath ;
62- var prefix = requestContext . Server . Options . UrlPrefixes . GetPrefix ( ( int ) requestContext . UrlContext ) ;
60+ var rawUrlInBytes = requestContext . GetRawUrlInBytes ( ) ;
61+ var originalPath = RequestUriBuilder . DecodeAndUnescapePath ( rawUrlInBytes ) ;
6362
64- // 'OPTIONS * HTTP/1.1'
65- if ( KnownMethod == HTTP_VERB . HttpVerbOPTIONS && string . Equals ( RawUrl , "*" , StringComparison . Ordinal ) )
66- {
6763 PathBase = string . Empty ;
68- Path = string . Empty ;
69- }
70- // Prefix may be null if the requested has been transferred to our queue
71- else if ( prefix is not null )
72- {
73- var pathBase = prefix . PathWithoutTrailingSlash ;
64+ Path = originalPath ;
65+ var prefix = requestContext . Server . Options . UrlPrefixes . GetPrefix ( ( int ) requestContext . UrlContext ) ;
7466
75- // url: /base/path, prefix: /base/, base: /base, path: /path
76- // url: /, prefix: /, base: , path: /
77- if ( originalPath . Equals ( pathBase , StringComparison . Ordinal ) )
78- {
79- // Exact match, no need to preserve the casing
80- PathBase = pathBase ;
81- Path = string . Empty ;
82- }
83- else if ( originalPath . Equals ( pathBase , StringComparison . OrdinalIgnoreCase ) )
67+ // 'OPTIONS * HTTP/1.1'
68+ if ( KnownMethod == HTTP_VERB . HttpVerbOPTIONS && string . Equals ( RawUrl , "*" , StringComparison . Ordinal ) )
8469 {
85- // Preserve the user input casing
86- PathBase = originalPath ;
70+ PathBase = string . Empty ;
8771 Path = string . Empty ;
8872 }
89- else if ( originalPath . StartsWith ( prefix . Path , StringComparison . Ordinal ) )
90- {
91- // Exact match, no need to preserve the casing
92- PathBase = pathBase ;
93- Path = originalPath [ pathBase . Length ..] ;
94- }
95- else if ( originalPath . StartsWith ( prefix . Path , StringComparison . OrdinalIgnoreCase ) )
73+ // Prefix may be null if the requested has been transferred to our queue
74+ else if ( prefix is not null )
9675 {
97- // Preserve the user input casing
98- PathBase = originalPath [ ..pathBase . Length ] ;
99- Path = originalPath [ pathBase . Length ..] ;
100- }
101- else
102- {
103- // Http.Sys path base matching is based on the cooked url which applies some non-standard normalizations that we don't use
104- // like collapsing duplicate slashes "//", converting '\' to '/', and un-escaping "%2F" to '/'. Find the right split and
105- // ignore the normalizations.
106- var originalOffset = 0 ;
107- var baseOffset = 0 ;
108- while ( originalOffset < originalPath . Length && baseOffset < pathBase . Length )
76+ var pathBase = prefix . PathWithoutTrailingSlash ;
77+
78+ // url: /base/path, prefix: /base/, base: /base, path: /path
79+ // url: /, prefix: /, base: , path: /
80+ if ( originalPath . Equals ( pathBase , StringComparison . Ordinal ) )
10981 {
110- var baseValue = pathBase [ baseOffset ] ;
111- var offsetValue = originalPath [ originalOffset ] ;
112- if ( baseValue == offsetValue
113- || char . ToUpperInvariant ( baseValue ) == char . ToUpperInvariant ( offsetValue ) )
114- {
115- // case-insensitive match, continue
116- originalOffset ++ ;
117- baseOffset ++ ;
118- }
119- else if ( baseValue == '/' && offsetValue == '\\ ' )
120- {
121- // Http.Sys considers these equivalent
122- originalOffset ++ ;
123- baseOffset ++ ;
124- }
125- else if ( baseValue == '/' && originalPath . AsSpan ( originalOffset ) . StartsWith ( "%2F" , StringComparison . OrdinalIgnoreCase ) )
126- {
127- // Http.Sys un-escapes this
128- originalOffset += 3 ;
129- baseOffset ++ ;
130- }
131- else if ( baseOffset > 0 && pathBase [ baseOffset - 1 ] == '/'
132- && ( offsetValue == '/' || offsetValue == '\\ ' ) )
133- {
134- // Duplicate slash, skip
135- originalOffset ++ ;
136- }
137- else if ( baseOffset > 0 && pathBase [ baseOffset - 1 ] == '/'
138- && originalPath . AsSpan ( originalOffset ) . StartsWith ( "%2F" , StringComparison . OrdinalIgnoreCase ) )
139- {
140- // Duplicate slash equivalent, skip
141- originalOffset += 3 ;
142- }
143- else
82+ // Exact match, no need to preserve the casing
83+ PathBase = pathBase ;
84+ Path = string . Empty ;
85+ }
86+ else if ( originalPath . Equals ( pathBase , StringComparison . OrdinalIgnoreCase ) )
87+ {
88+ // Preserve the user input casing
89+ PathBase = originalPath ;
90+ Path = string . Empty ;
91+ }
92+ else if ( originalPath . StartsWith ( prefix . Path , StringComparison . Ordinal ) )
93+ {
94+ // Exact match, no need to preserve the casing
95+ PathBase = pathBase ;
96+ Path = originalPath [ pathBase . Length ..] ;
97+ }
98+ else if ( originalPath . StartsWith ( prefix . Path , StringComparison . OrdinalIgnoreCase ) )
99+ {
100+ // Preserve the user input casing
101+ PathBase = originalPath [ ..pathBase . Length ] ;
102+ Path = originalPath [ pathBase . Length ..] ;
103+ }
104+ else
105+ {
106+ // Http.Sys path base matching is based on the cooked url which applies some non-standard normalizations that we don't use
107+ // like collapsing duplicate slashes "//", converting '\' to '/', and un-escaping "%2F" to '/'. Find the right split and
108+ // ignore the normalizations.
109+ var originalOffset = 0 ;
110+ var baseOffset = 0 ;
111+ while ( originalOffset < originalPath . Length && baseOffset < pathBase . Length )
144112 {
145- // Mismatch, fall back
146- // The failing test case here is "/base/call//../bat//path1//path2", reduced to "/base/call/bat//path1//path2",
147- // where http.sys collapses "//" before "../", but we do "../" first. We've lost the context that there were dot segments,
148- // or duplicate slashes, how do we figure out that "call/" can be eliminated?
149- originalOffset = 0 ;
150- break ;
113+ var baseValue = pathBase [ baseOffset ] ;
114+ var offsetValue = originalPath [ originalOffset ] ;
115+ if ( baseValue == offsetValue
116+ || char . ToUpperInvariant ( baseValue ) == char . ToUpperInvariant ( offsetValue ) )
117+ {
118+ // case-insensitive match, continue
119+ originalOffset ++ ;
120+ baseOffset ++ ;
121+ }
122+ else if ( baseValue == '/' && offsetValue == '\\ ' )
123+ {
124+ // Http.Sys considers these equivalent
125+ originalOffset ++ ;
126+ baseOffset ++ ;
127+ }
128+ else if ( baseValue == '/' && originalPath . AsSpan ( originalOffset ) . StartsWith ( "%2F" , StringComparison . OrdinalIgnoreCase ) )
129+ {
130+ // Http.Sys un-escapes this
131+ originalOffset += 3 ;
132+ baseOffset ++ ;
133+ }
134+ else if ( baseOffset > 0 && pathBase [ baseOffset - 1 ] == '/'
135+ && ( offsetValue == '/' || offsetValue == '\\ ' ) )
136+ {
137+ // Duplicate slash, skip
138+ originalOffset ++ ;
139+ }
140+ else if ( baseOffset > 0 && pathBase [ baseOffset - 1 ] == '/'
141+ && originalPath . AsSpan ( originalOffset ) . StartsWith ( "%2F" , StringComparison . OrdinalIgnoreCase ) )
142+ {
143+ // Duplicate slash equivalent, skip
144+ originalOffset += 3 ;
145+ }
146+ else
147+ {
148+ // Mismatch, fall back
149+ // The failing test case here is "/base/call//../bat//path1//path2", reduced to "/base/call/bat//path1//path2",
150+ // where http.sys collapses "//" before "../", but we do "../" first. We've lost the context that there were dot segments,
151+ // or duplicate slashes, how do we figure out that "call/" can be eliminated?
152+ originalOffset = 0 ;
153+ break ;
154+ }
151155 }
156+ PathBase = originalPath [ ..originalOffset ] ;
157+ Path = originalPath [ originalOffset ..] ;
152158 }
153- PathBase = originalPath [ ..originalOffset ] ;
154- Path = originalPath [ originalOffset ..] ;
155159 }
156- }
157- else if ( requestContext . Server . Options . UrlPrefixes . TryMatchLongestPrefix ( IsHttps , cookedUrl . GetHost ( ) ! , originalPath , out var pathBase , out var path ) )
158- {
159- PathBase = pathBase ;
160- Path = path ;
161- }
160+ else if ( requestContext . Server . Options . UrlPrefixes . TryMatchLongestPrefix ( IsHttps , cookedUrl . GetHost ( ) ! , originalPath , out var pathBase , out var path ) )
161+ {
162+ PathBase = pathBase ;
163+ Path = path ;
164+ }
162165
163- ProtocolVersion = RequestContext . GetVersion ( ) ;
166+ ProtocolVersion = RequestContext . GetVersion ( ) ;
164167
165- Headers = new RequestHeaders ( RequestContext ) ;
168+ Headers = new RequestHeaders ( RequestContext ) ;
166169
167- User = RequestContext . GetUser ( ) ;
170+ User = RequestContext . GetUser ( ) ;
168171
169- SniHostName = string . Empty ;
170- if ( IsHttps )
171- {
172- GetTlsHandshakeResults ( ) ;
173- }
172+ SniHostName = string . Empty ;
173+ if ( IsHttps )
174+ {
175+ GetTlsHandshakeResults ( ) ;
176+ }
174177
175- // GetTlsTokenBindingInfo(); TODO: https://github.com/aspnet/HttpSysServer/issues/231
178+ // GetTlsTokenBindingInfo(); TODO: https://github.com/aspnet/HttpSysServer/issues/231
176179
177- // Finished directly accessing the HTTP_REQUEST structure.
178- RequestContext . ReleasePins ( ) ;
179- // TODO: Verbose log parameters
180+ }
181+ finally
182+ {
183+ // Finished directly accessing the HTTP_REQUEST structure.
184+ RequestContext . ReleasePins ( ) ;
185+ // TODO: Verbose log parameters
186+ }
180187
181188 RemoveContentLengthIfTransferEncodingContainsChunked ( ) ;
182189 }
0 commit comments