diff --git a/src/Microsoft.Net.Http.Server/RequestProcessing/Request.cs b/src/Microsoft.Net.Http.Server/RequestProcessing/Request.cs index 43a77a54d1..924b7eef2c 100644 --- a/src/Microsoft.Net.Http.Server/RequestProcessing/Request.cs +++ b/src/Microsoft.Net.Http.Server/RequestProcessing/Request.cs @@ -64,6 +64,10 @@ namespace Microsoft.Net.Http.Server SslStatus = memoryBlob.RequestBlob->pSslInfo == null ? SslStatus.Insecure : memoryBlob.RequestBlob->pSslInfo->SslClientCertNegotiated == 0 ? SslStatus.NoClientCert : SslStatus.ClientCert; + + KnownMethod = memoryBlob.RequestBlob->Verb; + Method = HttpApi.GetVerb(memoryBlob.RequestBlob); + if (memoryBlob.RequestBlob->pRawUrl != null && memoryBlob.RequestBlob->RawUrlLength > 0) { RawUrl = Marshal.PtrToStringAnsi((IntPtr)memoryBlob.RequestBlob->pRawUrl, memoryBlob.RequestBlob->RawUrlLength); @@ -89,8 +93,14 @@ namespace Microsoft.Net.Http.Server var prefix = requestContext.Server.Settings.UrlPrefixes.GetPrefix((int)memoryBlob.RequestBlob->UrlContext); var originalPath = RequestUriBuilder.GetRequestPath(RawUrl, cookedUrlPath, RequestContext.Logger); + // 'OPTIONS * HTTP/1.1' + if (KnownMethod == HttpApi.HTTP_VERB.HttpVerbOPTIONS && string.Equals(RawUrl, "*", StringComparison.Ordinal)) + { + PathBase = string.Empty; + Path = string.Empty; + } // These paths are both unescaped already. - if (originalPath.Length == prefix.Path.Length - 1) + else if (originalPath.Length == prefix.Path.Length - 1) { // They matched exactly except for the trailing slash. PathBase = originalPath; @@ -119,8 +129,6 @@ namespace Microsoft.Net.Http.Server ProtocolVersion = new Version(major, minor); } - KnownMethod = memoryBlob.RequestBlob->Verb; - Method = HttpApi.GetVerb(memoryBlob.RequestBlob); Headers = new HeaderCollection(new RequestHeaders(_nativeRequestContext)); var requestV2 = (HttpApi.HTTP_REQUEST_V2*)memoryBlob.RequestBlob; diff --git a/test/Microsoft.Net.Http.Server.FunctionalTests/RequestTests.cs b/test/Microsoft.Net.Http.Server.FunctionalTests/RequestTests.cs index 31e5e850bf..fa28c81d5b 100644 --- a/test/Microsoft.Net.Http.Server.FunctionalTests/RequestTests.cs +++ b/test/Microsoft.Net.Http.Server.FunctionalTests/RequestTests.cs @@ -111,6 +111,21 @@ namespace Microsoft.Net.Http.Server } } + [Fact] + public async Task Request_OptionsStar_EmptyPath() + { + string root; + using (var server = Utilities.CreateHttpServerReturnRoot("/", out root)) + { + var responseTask = SendSocketRequestAsync(root, "*", "OPTIONS"); + var context = await server.AcceptAsync(); + Assert.Equal("", context.Request.PathBase); + Assert.Equal("", context.Request.Path); + Assert.Equal("*", context.Request.RawUrl); + context.Dispose(); + } + } + [Theory] // The test server defines these prefixes: "/", "/11", "/2/3", "/2", "/11/2" [InlineData("/", "", "/")] @@ -163,11 +178,11 @@ namespace Microsoft.Net.Http.Server } } - private async Task SendSocketRequestAsync(string address, string path) + private async Task SendSocketRequestAsync(string address, string path, string method = "GET") { var uri = new Uri(address); StringBuilder builder = new StringBuilder(); - builder.AppendLine("GET " + path + " HTTP/1.1"); + builder.AppendLine($"{method} {path} HTTP/1.1"); builder.AppendLine("Connection: close"); builder.Append("HOST: "); builder.AppendLine(uri.Authority);