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("/", "", "/")]