diff --git a/WebListener.sln b/WebListener.sln
index 5570995d52..52409addaa 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 8e72903252..fb4e8c070b 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 0000000000..ed2140ef02
--- /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 0000000000..fece76e422
--- /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 0000000000..f4d93ce1af
--- /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