diff --git a/shared/Microsoft.AspNetCore.HttpSys.Sources/RequestProcessing/RawUrlHelper.cs b/shared/Microsoft.AspNetCore.HttpSys.Sources/RequestProcessing/RawUrlHelper.cs
index 7402c4ef8f..ee0bf4a996 100644
--- a/shared/Microsoft.AspNetCore.HttpSys.Sources/RequestProcessing/RawUrlHelper.cs
+++ b/shared/Microsoft.AspNetCore.HttpSys.Sources/RequestProcessing/RawUrlHelper.cs
@@ -2,11 +2,14 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Text;
namespace Microsoft.AspNetCore.HttpSys.Internal
{
internal static class RawUrlHelper
{
+ private static readonly byte[] _forwardSlashPath = Encoding.ASCII.GetBytes("/");
+
///
/// Find the segment of the URI byte array which represents the path.
///
@@ -39,7 +42,8 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
if (pathStartIndex == -1)
{
// e.g. for request lines like: 'GET http://myserver' (no final '/')
- pathStartIndex = raw.Length;
+ // At this point we can return a path with a slash.
+ return new ArraySegment(_forwardSlashPath);
}
}
else
diff --git a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/RequestTests.cs b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/RequestTests.cs
index 72ba944f74..20ea25c514 100644
--- a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/RequestTests.cs
+++ b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/Listener/RequestTests.cs
@@ -114,6 +114,23 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener
}
}
+ [ConditionalFact]
+ public async Task Request_FullUriInRequestLine_ParsesPath()
+ {
+ string root;
+ using (var server = Utilities.CreateHttpServerReturnRoot("/", out root))
+ {
+ // Send a HTTP request with the request line:
+ // GET http://localhost:5001 HTTP/1.1
+ var responseTask = SendSocketRequestAsync(root, root);
+ var context = await server.AcceptAsync(Utilities.DefaultTimeout);
+ Assert.Equal("/", context.Request.Path);
+ Assert.Equal("", context.Request.PathBase);
+ Assert.Equal(root, context.Request.RawUrl);
+ Assert.False(root.EndsWith("/")); // make sure root doesn't have a trailing slash
+ }
+ }
+
[ConditionalFact]
public async Task Request_OptionsStar_EmptyPath()
{
diff --git a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/RequestTests.cs b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/RequestTests.cs
index 38ec6d0cb2..837c53340a 100644
--- a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/RequestTests.cs
+++ b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/RequestTests.cs
@@ -259,6 +259,42 @@ namespace Microsoft.AspNetCore.Server.HttpSys
}
}
+ [ConditionalFact]
+ public async Task Request_FullUriInRequestLine_ParsesPath()
+ {
+ using (var server = Utilities.CreateHttpServerReturnRoot("/", out var root, httpContext =>
+ {
+ var requestInfo = httpContext.Features.Get();
+ Assert.Equal("/", requestInfo.Path);
+ Assert.Equal("", requestInfo.PathBase);
+ return Task.CompletedTask;
+ }))
+ {
+ // Send a HTTP request with the request line:
+ // GET http://localhost:5001 HTTP/1.1
+ var response = await SendSocketRequestAsync(root, root);
+ var responseStatusCode = response.Substring(9); // Skip "HTTP/1.1 "
+ Assert.Equal(StatusCodes.Status200OK.ToString(), responseStatusCode);
+ }
+ }
+
+ [ConditionalFact]
+ public async Task Request_FullUriInRequestLineWithSlashesInQuery_BlockedByHttpSys()
+ {
+ using (var server = Utilities.CreateHttpServerReturnRoot("/", out var root, httpContext =>
+ {
+ return Task.CompletedTask;
+ }))
+ {
+ // Send a HTTP request with the request line:
+ // GET http://localhost:5001?query=value/1/2 HTTP/1.1
+ // Should return a 400 as it is a client error
+ var response = await SendSocketRequestAsync(root, root + "?query=value/1/2");
+ var responseStatusCode = response.Substring(9); // Skip "HTTP/1.1 "
+ Assert.Equal(StatusCodes.Status400BadRequest.ToString(), responseStatusCode);
+ }
+ }
+
[ConditionalTheory]
// The test server defines these prefixes: "/", "/11", "/2/3", "/2", "/11/2"
[InlineData("/", "", "/")]