@@ -1030,42 +1030,27 @@ public RequestLineStatus TakeStartLine(SocketInput input)
1030
1030
// begin consuming request-target
1031
1031
begin = scan ;
1032
1032
1033
- var targetIsAbsolute = false ;
1034
- string requestUriScheme ;
1035
- string requestAuthority = null ;
1036
- if ( scan . GetKnownHttpSchema ( out requestUriScheme ) )
1037
- {
1038
- // Start-line can contain uri in absolute form. e.g. 'GET http://contoso.com/favicon.ico HTTP/1.1'
1039
- // Clients should only send this to proxies, but the spec requires we handle it anyways.
1040
- // cref https://tools.ietf.org/html/rfc7230#section-5.3
1041
-
1042
- // This will skip over scheme and authority so they do not end up in .Path,
1043
- // but preserving the values for rawTarget. We rely on the Host header and
1044
- // server configuration to determine the effective host, port, and
1045
- // for this request.
1046
-
1047
- targetIsAbsolute = true ;
1048
- scan . Skip ( requestUriScheme . Length ) ;
1049
- begin = scan ;
1033
+ var targetBegin = scan ;
1050
1034
1051
- // an absolute URI is not required to end in a slash but host must not be empty
1052
- // see https://tools.ietf.org/html/rfc3986#section-4.3
1053
- var chNext = scan . Peek ( ) ;
1054
- if ( chNext == ByteForwardSlash || chNext == ByteSpace )
1035
+ if ( targetBegin . Peek ( ) != ByteForwardSlash )
1036
+ {
1037
+ string requestUriScheme ;
1038
+ if ( scan . GetKnownHttpSchema ( out requestUriScheme ) )
1055
1039
{
1056
- RejectRequest ( RequestRejectionReason . InvalidRequestLine ,
1057
- Log . IsEnabled ( LogLevel . Information ) ? start . GetAsciiStringEscaped ( end , MaxInvalidRequestLineChars ) : string . Empty ) ;
1058
- }
1040
+ // Start-line can contain uri in absolute form. e.g. 'GET http://contoso.com/favicon.ico HTTP/1.1'
1041
+ // Clients should only send this to proxies, but the spec requires we handle it anyways.
1042
+ // cref https://tools.ietf.org/html/rfc7230#section-5.3
1043
+
1044
+ scan . Skip ( requestUriScheme . Length ) ;
1059
1045
1060
- if ( scan . Seek ( ByteForwardSlash , ByteSpace , ref end ) == - 1 )
1061
- {
1062
- RejectRequest ( RequestRejectionReason . InvalidRequestLine ,
1063
- Log . IsEnabled ( LogLevel . Information ) ? start . GetAsciiStringEscaped ( end , MaxInvalidRequestLineChars ) : string . Empty ) ;
1046
+ if ( scan . Seek ( ByteForwardSlash , ByteSpace , ref end ) == - 1 )
1047
+ {
1048
+ RejectRequest ( RequestRejectionReason . InvalidRequestLine ,
1049
+ Log . IsEnabled ( LogLevel . Information ) ? start . GetAsciiStringEscaped ( end , MaxInvalidRequestLineChars ) : string . Empty ) ;
1050
+ }
1051
+
1052
+ begin = scan ;
1064
1053
}
1065
-
1066
- // TODO consider handling UTF-8 host names
1067
- requestAuthority = begin . GetAsciiString ( ref scan ) ;
1068
- begin = scan ;
1069
1054
}
1070
1055
1071
1056
var needDecode = false ;
@@ -1089,7 +1074,7 @@ public RequestLineStatus TakeStartLine(SocketInput input)
1089
1074
var pathBegin = begin ;
1090
1075
var pathEnd = scan ;
1091
1076
1092
- var queryString = "" ;
1077
+ var queryString = string . Empty ;
1093
1078
if ( chFound == ByteQuestionMark )
1094
1079
{
1095
1080
begin = scan ;
@@ -1103,7 +1088,7 @@ public RequestLineStatus TakeStartLine(SocketInput input)
1103
1088
1104
1089
var queryEnd = scan ;
1105
1090
1106
- if ( pathBegin . Peek ( ) == ByteSpace && ! targetIsAbsolute )
1091
+ if ( pathBegin . Peek ( ) == ByteSpace && targetBegin . Index == pathBegin . Index )
1107
1092
{
1108
1093
RejectRequest ( RequestRejectionReason . InvalidRequestLine ,
1109
1094
Log . IsEnabled ( LogLevel . Information ) ? start . GetAsciiStringEscaped ( end , MaxInvalidRequestLineChars ) : string . Empty ) ;
@@ -1146,11 +1131,11 @@ public RequestLineStatus TakeStartLine(SocketInput input)
1146
1131
// Multibyte Internationalized Resource Identifiers (IRIs) are first converted to utf8;
1147
1132
// then encoded/escaped to ASCII https://www.ietf.org/rfc/rfc3987.txt "Mapping of IRIs to URIs"
1148
1133
string requestUrlPath ;
1149
- string rawUrlPath ;
1134
+ string rawTarget ;
1150
1135
if ( needDecode )
1151
1136
{
1152
1137
// Read raw target before mutating memory.
1153
- rawUrlPath = pathBegin . GetAsciiString ( ref queryEnd ) ;
1138
+ rawTarget = targetBegin . GetAsciiString ( ref queryEnd ) ;
1154
1139
1155
1140
// URI was encoded, unescape and then parse as utf8
1156
1141
pathEnd = UrlPathDecoder . Unescape ( pathBegin , pathEnd ) ;
@@ -1161,15 +1146,30 @@ public RequestLineStatus TakeStartLine(SocketInput input)
1161
1146
// URI wasn't encoded, parse as ASCII
1162
1147
requestUrlPath = pathBegin . GetAsciiString ( ref pathEnd ) ;
1163
1148
1164
- if ( queryString . Length == 0 )
1149
+ if ( queryString . Length == 0 && targetBegin . Index == pathBegin . Index )
1165
1150
{
1166
1151
// No need to allocate an extra string if the path didn't need
1167
- // decoding and there's no query string following it.
1168
- rawUrlPath = requestUrlPath ;
1152
+ // decoding and there's no query string following it, and the
1153
+ // request-target isn't absolute-form
1154
+ rawTarget = requestUrlPath ;
1169
1155
}
1170
1156
else
1171
1157
{
1172
- rawUrlPath = pathBegin . GetAsciiString ( ref queryEnd ) ;
1158
+ rawTarget = targetBegin . GetAsciiString ( ref queryEnd ) ;
1159
+ }
1160
+ }
1161
+
1162
+ if ( targetBegin . Index < pathBegin . Index )
1163
+ {
1164
+ // validation of absolute-form URI may be slow, but clients
1165
+ // should not be sending this form anyways, so perf optimization
1166
+ // not high priority
1167
+
1168
+ Uri _ ;
1169
+ if ( ! Uri . TryCreate ( rawTarget , UriKind . Absolute , out _ ) )
1170
+ {
1171
+ RejectRequest ( RequestRejectionReason . InvalidRequestLine ,
1172
+ Log . IsEnabled ( LogLevel . Information ) ? start . GetAsciiStringEscaped ( end , MaxInvalidRequestLineChars ) : string . Empty ) ;
1173
1173
}
1174
1174
}
1175
1175
@@ -1180,9 +1180,7 @@ public RequestLineStatus TakeStartLine(SocketInput input)
1180
1180
consumed = scan ;
1181
1181
Method = method ;
1182
1182
QueryString = queryString ;
1183
- RawTarget = targetIsAbsolute
1184
- ? requestUriScheme + requestAuthority + rawUrlPath
1185
- : rawUrlPath ;
1183
+ RawTarget = rawTarget ;
1186
1184
HttpVersion = httpVersion ;
1187
1185
1188
1186
bool caseMatches ;
@@ -1191,7 +1189,7 @@ public RequestLineStatus TakeStartLine(SocketInput input)
1191
1189
PathBase = caseMatches ? _pathBase : normalizedUrlPath . Substring ( 0 , _pathBase . Length ) ;
1192
1190
Path = normalizedUrlPath . Substring ( _pathBase . Length ) ;
1193
1191
}
1194
- else if ( rawUrlPath ? . Length > 0 && rawUrlPath [ 0 ] == '/' ) // check rawUrlPath since normalizedUrlPath can be "" or "/" after dot segment removal
1192
+ else if ( requestUrlPath ? . Length > 0 && requestUrlPath [ 0 ] == '/' ) // check requestUrlPath since normalizedUrlPath can be "" or "/" after dot segment removal
1195
1193
{
1196
1194
Path = normalizedUrlPath ;
1197
1195
}
0 commit comments