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