From 07b078d4e37ffd20279865e38a86a815ab4cd429 Mon Sep 17 00:00:00 2001 From: John Luo Date: Wed, 13 Jul 2016 17:10:12 -0700 Subject: [PATCH] Remove support for specifying only ports in IServerAddresses #197 --- WebListener.sln | 18 +++- src/Microsoft.Net.Http.Server/UrlPrefix.cs | 53 ++++++------ .../Microsoft.Net.Http.Server.Tests.xproj | 20 +++++ .../UrlPrefixTests.cs | 85 +++++++++++++++++++ .../project.json | 19 +++++ 5 files changed, 167 insertions(+), 28 deletions(-) create mode 100644 test/Microsoft.Net.Http.Server.Tests/Microsoft.Net.Http.Server.Tests.xproj create mode 100644 test/Microsoft.Net.Http.Server.Tests/UrlPrefixTests.cs create mode 100644 test/Microsoft.Net.Http.Server.Tests/project.json diff --git a/WebListener.sln b/WebListener.sln index 5570995..52409ad 100644 --- a/WebListener.sln +++ b/WebListener.sln @@ -1,7 +1,6 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.23107.0 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestClient", "samples\TestClient\TestClient.csproj", "{8B828433-B333-4C19-96AE-00BFFF9D8841}" EndProject @@ -32,6 +31,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "HotAddSample", "samples\HotAddSample\HotAddSample.xproj", "{8BFA392A-8B67-4454-916B-67C545EDFAEF}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Net.Http.Server.Tests", "test\Microsoft.Net.Http.Server.Tests\Microsoft.Net.Http.Server.Tests.xproj", "{E837249E-E666-4DF2-AFC3-7A4D70234F9F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -134,6 +135,18 @@ Global {8BFA392A-8B67-4454-916B-67C545EDFAEF}.Release|Mixed Platforms.Build.0 = Release|Any CPU {8BFA392A-8B67-4454-916B-67C545EDFAEF}.Release|x86.ActiveCfg = Release|Any CPU {8BFA392A-8B67-4454-916B-67C545EDFAEF}.Release|x86.Build.0 = Release|Any CPU + {E837249E-E666-4DF2-AFC3-7A4D70234F9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E837249E-E666-4DF2-AFC3-7A4D70234F9F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E837249E-E666-4DF2-AFC3-7A4D70234F9F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {E837249E-E666-4DF2-AFC3-7A4D70234F9F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {E837249E-E666-4DF2-AFC3-7A4D70234F9F}.Debug|x86.ActiveCfg = Debug|Any CPU + {E837249E-E666-4DF2-AFC3-7A4D70234F9F}.Debug|x86.Build.0 = Debug|Any CPU + {E837249E-E666-4DF2-AFC3-7A4D70234F9F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E837249E-E666-4DF2-AFC3-7A4D70234F9F}.Release|Any CPU.Build.0 = Release|Any CPU + {E837249E-E666-4DF2-AFC3-7A4D70234F9F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {E837249E-E666-4DF2-AFC3-7A4D70234F9F}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {E837249E-E666-4DF2-AFC3-7A4D70234F9F}.Release|x86.ActiveCfg = Release|Any CPU + {E837249E-E666-4DF2-AFC3-7A4D70234F9F}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -148,5 +161,6 @@ Global {B9F45F9D-D206-47F0-8E5F-54CE2F0BDF92} = {99D5E5F3-88F5-4CCF-8D8C-717C8925DF09} {DCB6E0B1-223D-44E6-8696-4767E5B6E6A1} = {E183C826-1360-4DFF-9994-F33CED5C8525} {8BFA392A-8B67-4454-916B-67C545EDFAEF} = {3A1E31E3-2794-4CA3-B8E2-253E96BDE514} + {E837249E-E666-4DF2-AFC3-7A4D70234F9F} = {E183C826-1360-4DFF-9994-F33CED5C8525} EndGlobalSection EndGlobal diff --git a/src/Microsoft.Net.Http.Server/UrlPrefix.cs b/src/Microsoft.Net.Http.Server/UrlPrefix.cs index 8e72903..fb4e8c0 100644 --- a/src/Microsoft.Net.Http.Server/UrlPrefix.cs +++ b/src/Microsoft.Net.Http.Server/UrlPrefix.cs @@ -117,49 +117,50 @@ namespace Microsoft.Net.Http.Server string host = null; int? port = null; string path = null; - string whole = prefix ?? string.Empty; + var whole = prefix ?? string.Empty; - int delimiterStart1 = whole.IndexOf("://", StringComparison.Ordinal); - if (delimiterStart1 < 0) + var schemeDelimiterEnd = whole.IndexOf("://", StringComparison.Ordinal); + if (schemeDelimiterEnd < 0) { - int aPort; - if (int.TryParse(whole, NumberStyles.None, CultureInfo.InvariantCulture, out aPort)) - { - return UrlPrefix.Create("http", "localhost", aPort, "/"); - } - throw new FormatException("Invalid prefix, missing scheme separator: " + prefix); } - int delimiterEnd1 = delimiterStart1 + "://".Length; + var hostDelimiterStart = schemeDelimiterEnd + "://".Length; - int delimiterStart3 = whole.IndexOf("/", delimiterEnd1, StringComparison.Ordinal); - if (delimiterStart3 < 0) + var pathDelimiterStart = whole.IndexOf("/", hostDelimiterStart, StringComparison.Ordinal); + if (pathDelimiterStart < 0) { - delimiterStart3 = whole.Length; + pathDelimiterStart = whole.Length; } - int delimiterStart2 = whole.LastIndexOf(":", delimiterStart3 - 1, delimiterStart3 - delimiterEnd1, StringComparison.Ordinal); - int delimiterEnd2 = delimiterStart2 + ":".Length; - if (delimiterStart2 < 0) + var hostDelimiterEnd = whole.LastIndexOf(":", pathDelimiterStart - 1, pathDelimiterStart - hostDelimiterStart, StringComparison.Ordinal); + if (hostDelimiterEnd < 0) { - delimiterStart2 = delimiterStart3; - delimiterEnd2 = delimiterStart3; + hostDelimiterEnd = pathDelimiterStart; } - scheme = whole.Substring(0, delimiterStart1); - string portString = whole.Substring(delimiterEnd2, delimiterStart3 - delimiterEnd2); + scheme = whole.Substring(0, schemeDelimiterEnd); + var portString = whole.Substring(hostDelimiterEnd, pathDelimiterStart - hostDelimiterEnd); // The leading ":" is included int portValue; - if (int.TryParse(portString, NumberStyles.Integer, CultureInfo.InvariantCulture, out portValue)) + if (!string.IsNullOrEmpty(portString)) { - host = whole.Substring(delimiterEnd1, delimiterStart2 - delimiterEnd1); - port = portValue; + var portValueString = portString.Substring(1); // Trim the leading ":" + if (int.TryParse(portValueString, NumberStyles.Integer, CultureInfo.InvariantCulture, out portValue)) + { + host = whole.Substring(hostDelimiterStart, hostDelimiterEnd - hostDelimiterStart); + port = portValue; + } + else + { + // This means a port was specified but was invalid or empty. + throw new FormatException("Invalid prefix, invalid port speficification: " + prefix); + } } else { - host = whole.Substring(delimiterEnd1, delimiterStart3 - delimiterEnd1); + host = whole.Substring(hostDelimiterStart, pathDelimiterStart - hostDelimiterStart); } - path = whole.Substring(delimiterStart3); + path = whole.Substring(pathDelimiterStart); - return UrlPrefix.Create(scheme, host, port, path); + return Create(scheme, host, port, path); } public bool IsHttps { get; private set; } diff --git a/test/Microsoft.Net.Http.Server.Tests/Microsoft.Net.Http.Server.Tests.xproj b/test/Microsoft.Net.Http.Server.Tests/Microsoft.Net.Http.Server.Tests.xproj new file mode 100644 index 0000000..ed2140e --- /dev/null +++ b/test/Microsoft.Net.Http.Server.Tests/Microsoft.Net.Http.Server.Tests.xproj @@ -0,0 +1,20 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + e837249e-e666-4df2-afc3-7a4d70234f9f + .\obj + .\bin\ + + + 2.0 + + + + + + \ No newline at end of file diff --git a/test/Microsoft.Net.Http.Server.Tests/UrlPrefixTests.cs b/test/Microsoft.Net.Http.Server.Tests/UrlPrefixTests.cs new file mode 100644 index 0000000..fece76e --- /dev/null +++ b/test/Microsoft.Net.Http.Server.Tests/UrlPrefixTests.cs @@ -0,0 +1,85 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Text; +using Xunit; + +namespace Microsoft.Net.Http.Server +{ + public class UrlPrefixTests + { + [Theory] + [InlineData("")] + [InlineData("5000")] + [InlineData("//noscheme")] + public void CreateThrowsForUrlsWithoutSchemeDelimiter(string url) + { + Assert.Throws(() => UrlPrefix.Create(url)); + } + + [Theory] + [InlineData("://emptyscheme")] + [InlineData("://")] + [InlineData("://:5000")] + public void CreateThrowsForUrlsWithEmptyScheme(string url) + { + Assert.Throws(() => UrlPrefix.Create(url)); + } + + [Theory] + [InlineData("http://")] + [InlineData("http://:5000")] + [InlineData("http:///")] + [InlineData("http:///:5000")] + [InlineData("http:////")] + [InlineData("http:////:5000")] + public void CreateThrowsForUrlsWithoutHost(string url) + { + Assert.Throws(() => UrlPrefix.Create(url)); + } + + [Theory] + [InlineData("http://www.example.com:NOTAPORT")] + [InlineData("https://www.example.com:NOTAPORT")] + [InlineData("http://www.example.com:NOTAPORT/")] + [InlineData("http://foo:/tmp/weblistener-test.sock:5000/doesn't/matter")] + public void CreateThrowsForUrlsWithInvalidPorts(string url) + { + Assert.Throws(() => UrlPrefix.Create(url)); + } + + [Theory] + [InlineData("http://+", "http", "+", "80", "/", "http://+:80/")] + [InlineData("http://*", "http", "*", "80", "/", "http://*:80/")] + [InlineData("http://localhost", "http", "localhost", "80", "/", "http://localhost:80/")] + [InlineData("http://www.example.com", "http", "www.example.com", "80", "/", "http://www.example.com:80/")] + [InlineData("https://www.example.com", "https", "www.example.com", "443", "/", "https://www.example.com:443/")] + [InlineData("http://www.example.com/", "http", "www.example.com", "80", "/", "http://www.example.com:80/")] + [InlineData("http://www.example.com/foo?bar=baz", "http", "www.example.com", "80", "/foo?bar=baz/", "http://www.example.com:80/foo?bar=baz/")] + [InlineData("http://www.example.com:5000", "http", "www.example.com", "5000", "/", "http://www.example.com:5000/")] + [InlineData("https://www.example.com:5000", "https", "www.example.com", "5000", "/", "https://www.example.com:5000/")] + [InlineData("http://www.example.com:5000/", "http", "www.example.com", "5000", "/", "http://www.example.com:5000/")] + [InlineData("http://www.example.com/foo:bar", "http", "www.example.com", "80", "/foo:bar/", "http://www.example.com:80/foo:bar/")] + public void UrlsAreParsedCorrectly(string url, string scheme, string host, string port, string pathBase, string toString) + { + var urlPrefix = UrlPrefix.Create(url); + + Assert.Equal(scheme, urlPrefix.Scheme); + Assert.Equal(host, urlPrefix.Host); + Assert.Equal(port, urlPrefix.Port); + Assert.Equal(pathBase, urlPrefix.Path); + + Assert.Equal(toString ?? url, urlPrefix.ToString()); + } + + [Fact] + public void PathBaseIsNotNormalized() + { + var urlPrefix = UrlPrefix.Create("http://localhost:8080/p\u0041\u030Athbase"); + + Assert.False(urlPrefix.Path.IsNormalized(NormalizationForm.FormC)); + Assert.Equal("/p\u0041\u030Athbase/", urlPrefix.Path); + } + } +} diff --git a/test/Microsoft.Net.Http.Server.Tests/project.json b/test/Microsoft.Net.Http.Server.Tests/project.json new file mode 100644 index 0000000..f4d93ce --- /dev/null +++ b/test/Microsoft.Net.Http.Server.Tests/project.json @@ -0,0 +1,19 @@ +{ + "testRunner": "xunit", + "dependencies": { + "dotnet-test-xunit": "2.2.0-*", + "Microsoft.Net.Http.Server": "0.2.0-*", + "xunit": "2.2.0-*" + }, + "frameworks": { + "netcoreapp1.0": { + "dependencies": { + "Microsoft.NETCore.App": { + "version": "1.0.0-*", + "type": "platform" + } + } + }, + "net451": { } + } +} \ No newline at end of file