Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Commit 58c3d0a

Browse files
author
Nate McMaster
committed
Handle requests using absolute-form request URIs.
An absolute-form request URI has a start line in form: "GET http://host/path HTTP/1.1". RFC 7230 section 5.3.2 stipulates that servers should allow absolute-form request URIs. This change will handles requests using absolute-form. The scheme and authority section of the absolute URI are ignored, but will still appear in IHttpRequestFeature.RawTarget. Resolves #666
1 parent 1294c00 commit 58c3d0a

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed

test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/RequestTests.cs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,72 @@ public async Task RequestAbortedTokenFiredOnClientFIN()
454454
}
455455
}
456456

457+
[Theory]
458+
[InlineData("http://localhost/abs/path", "/abs/path", null)]
459+
[InlineData("https://localhost/abs/path", "/abs/path", null)] // handles mismatch scheme
460+
[InlineData("https://localhost:22/abs/path", "/abs/path", null)] // handles mismatched ports
461+
[InlineData("https://differenthost/abs/path", "/abs/path", null)] // handles mismatched hostname
462+
[InlineData("http://localhost/", "/", null)]
463+
[InlineData("http://[email protected]/path", "/path", null)]
464+
[InlineData("http://root:[email protected]/path", "/path", null)]
465+
[InlineData("https://localhost/", "/", null)]
466+
[InlineData("http://localhost", "/", null)]
467+
[InlineData("http://127.0.0.1/", "/", null)]
468+
[InlineData("http://[::1]/", "/", null)]
469+
[InlineData("http://[::1]:8080/", "/", null)]
470+
[InlineData("http://localhost?q=123&w=xyz", "/", "123")]
471+
[InlineData("http://localhost/?q=123&w=xyz", "/", "123")]
472+
[InlineData("http://localhost/path?q=123&w=xyz", "/path", "123")]
473+
[InlineData("http://localhost/path%20with%20space?q=abc%20123", "/path with space", "abc 123")]
474+
public async Task CanHandleRequestsWithUrlInAbsoluteForm(string requestUrl, string expectedPath, string queryValue)
475+
{
476+
var pathTcs = new TaskCompletionSource<PathString>();
477+
var rawTargetTcs = new TaskCompletionSource<string>();
478+
var hostTcs = new TaskCompletionSource<HostString>();
479+
var queryTcs = new TaskCompletionSource<IQueryCollection>();
480+
481+
using (var server = new TestServer(async context =>
482+
{
483+
pathTcs.TrySetResult(context.Request.Path);
484+
hostTcs.TrySetResult(context.Request.Host);
485+
queryTcs.TrySetResult(context.Request.Query);
486+
rawTargetTcs.TrySetResult(context.Features.Get<IHttpRequestFeature>().RawTarget);
487+
await context.Response.WriteAsync("Done");
488+
}))
489+
{
490+
using (var connection = server.CreateConnection())
491+
{
492+
await connection.Send(
493+
$"GET {requestUrl} HTTP/1.1",
494+
"Content-Length: 0",
495+
"Host: localhost",
496+
"",
497+
"");
498+
499+
await connection.Receive($"HTTP/1.1 200 OK",
500+
$"Date: {server.Context.DateHeaderValue}",
501+
"Transfer-Encoding: chunked",
502+
"",
503+
"4",
504+
"Done")
505+
.TimeoutAfter(TimeSpan.FromSeconds(10));
506+
507+
await Task.WhenAll(pathTcs.Task, rawTargetTcs.Task, hostTcs.Task, queryTcs.Task).TimeoutAfter(TimeSpan.FromSeconds(30));
508+
Assert.Equal(new PathString(expectedPath), pathTcs.Task.Result);
509+
Assert.Equal(requestUrl, rawTargetTcs.Task.Result);
510+
Assert.Equal("localhost", hostTcs.Task.Result.ToString());
511+
if (queryValue == null)
512+
{
513+
Assert.False(queryTcs.Task.Result.ContainsKey("q"));
514+
}
515+
else
516+
{
517+
Assert.Equal(queryValue, queryTcs.Task.Result["q"]);
518+
}
519+
}
520+
}
521+
}
522+
457523
private async Task TestRemoteIPAddress(string registerAddress, string requestAddress, string expectAddress)
458524
{
459525
var builder = new WebHostBuilder()

test/Microsoft.AspNetCore.Server.KestrelTests/FrameTests.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,35 @@ public async Task TakeStartLineReturnsWhenGivenIncompleteRequestLines(string req
477477
Assert.False(returnValue);
478478
}
479479

480+
[Theory]
481+
[InlineData("http://host/abs/path", "/abs/path")]
482+
[InlineData("https://host/abs/path", "/abs/path")]
483+
[InlineData("https://host:22/abs/path", "/abs/path")]
484+
[InlineData("https://user@host:9080/abs/path", "/abs/path")]
485+
[InlineData("http://host/", "/")]
486+
[InlineData("https://host/", "/")]
487+
[InlineData("http://host", "/")]
488+
[InlineData("http://user@host/", "/")]
489+
[InlineData("http://127.0.0.1/", "/")]
490+
[InlineData("http://[email protected]/", "/")]
491+
[InlineData("http://[email protected]:8080/", "/")]
492+
[InlineData("http://127.0.0.1:8080/", "/")]
493+
[InlineData("http://[::1]", "/")]
494+
[InlineData("http://[::1]/path", "/path")]
495+
[InlineData("http://[::1]:8080/", "/")]
496+
[InlineData("http://user@[::1]:8080/", "/")]
497+
public void TakeStartLineHandlesRequestTarget(string rawTarget, string expectedPath)
498+
{
499+
var firstLine = Encoding.ASCII.GetBytes($"GET {rawTarget} HTTP/1.1\r\n");
500+
_socketInput.IncomingData(firstLine, 0, firstLine.Length);
501+
502+
var status = _frame.TakeStartLine(_socketInput);
503+
504+
Assert.Equal(Frame.RequestLineStatus.Done, status);
505+
Assert.Equal(expectedPath, _frame.Path);
506+
Assert.Equal(rawTarget, _frame.RawTarget);
507+
}
508+
480509
[Fact]
481510
public async Task ParseRequestStartsRequestHeadersTimeoutOnFirstByteAvailable()
482511
{

0 commit comments

Comments
 (0)