Add BindingAddress to HttpAbstractions (#977)

This commit is contained in:
Justin Kotalik 2017-12-18 18:40:11 -08:00 committed by GitHub
parent d104129d57
commit 150bb3faf4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 226 additions and 0 deletions

View File

@ -0,0 +1,155 @@
// 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.Diagnostics;
using System.Globalization;
namespace Microsoft.AspNetCore.Http.Internal
{
public class BindingAddress
{
public string Host { get; private set; }
public string PathBase { get; private set; }
public int Port { get; internal set; }
public string Scheme { get; private set; }
public bool IsUnixPipe
{
get
{
return Host.StartsWith(Constants.UnixPipeHostPrefix, StringComparison.Ordinal);
}
}
public string UnixPipePath
{
get
{
if (!IsUnixPipe)
{
throw new InvalidOperationException("Binding address is not a unix pipe.");
}
return Host.Substring(Constants.UnixPipeHostPrefix.Length - 1);
}
}
public override string ToString()
{
if (IsUnixPipe)
{
return Scheme.ToLowerInvariant() + "://" + Host.ToLowerInvariant();
}
else
{
return Scheme.ToLowerInvariant() + "://" + Host.ToLowerInvariant() + ":" + Port.ToString(CultureInfo.InvariantCulture) + PathBase.ToString(CultureInfo.InvariantCulture);
}
}
public override int GetHashCode()
{
return ToString().GetHashCode();
}
public override bool Equals(object obj)
{
var other = obj as BindingAddress;
if (other == null)
{
return false;
}
return string.Equals(Scheme, other.Scheme, StringComparison.OrdinalIgnoreCase)
&& string.Equals(Host, other.Host, StringComparison.OrdinalIgnoreCase)
&& Port == other.Port
&& PathBase == other.PathBase;
}
public static BindingAddress Parse(string address)
{
address = address ?? string.Empty;
int schemeDelimiterStart = address.IndexOf("://", StringComparison.Ordinal);
if (schemeDelimiterStart < 0)
{
throw new FormatException($"Invalid url: '{address}'");
}
int schemeDelimiterEnd = schemeDelimiterStart + "://".Length;
var isUnixPipe = address.IndexOf(Constants.UnixPipeHostPrefix, schemeDelimiterEnd, StringComparison.Ordinal) == schemeDelimiterEnd;
int pathDelimiterStart;
int pathDelimiterEnd;
if (!isUnixPipe)
{
pathDelimiterStart = address.IndexOf("/", schemeDelimiterEnd, StringComparison.Ordinal);
pathDelimiterEnd = pathDelimiterStart;
}
else
{
pathDelimiterStart = address.IndexOf(":", schemeDelimiterEnd + Constants.UnixPipeHostPrefix.Length, StringComparison.Ordinal);
pathDelimiterEnd = pathDelimiterStart + ":".Length;
}
if (pathDelimiterStart < 0)
{
pathDelimiterStart = pathDelimiterEnd = address.Length;
}
var serverAddress = new BindingAddress();
serverAddress.Scheme = address.Substring(0, schemeDelimiterStart);
var hasSpecifiedPort = false;
if (!isUnixPipe)
{
int portDelimiterStart = address.LastIndexOf(":", pathDelimiterStart - 1, pathDelimiterStart - schemeDelimiterEnd, StringComparison.Ordinal);
if (portDelimiterStart >= 0)
{
int portDelimiterEnd = portDelimiterStart + ":".Length;
string portString = address.Substring(portDelimiterEnd, pathDelimiterStart - portDelimiterEnd);
int portNumber;
if (int.TryParse(portString, NumberStyles.Integer, CultureInfo.InvariantCulture, out portNumber))
{
hasSpecifiedPort = true;
serverAddress.Host = address.Substring(schemeDelimiterEnd, portDelimiterStart - schemeDelimiterEnd);
serverAddress.Port = portNumber;
}
}
if (!hasSpecifiedPort)
{
if (string.Equals(serverAddress.Scheme, "http", StringComparison.OrdinalIgnoreCase))
{
serverAddress.Port = 80;
}
else if (string.Equals(serverAddress.Scheme, "https", StringComparison.OrdinalIgnoreCase))
{
serverAddress.Port = 443;
}
}
}
if (!hasSpecifiedPort)
{
serverAddress.Host = address.Substring(schemeDelimiterEnd, pathDelimiterStart - schemeDelimiterEnd);
}
if (string.IsNullOrEmpty(serverAddress.Host))
{
throw new FormatException($"Invalid url: '{address}'");
}
if (address[address.Length - 1] == '/')
{
serverAddress.PathBase = address.Substring(pathDelimiterEnd, address.Length - pathDelimiterEnd - 1);
}
else
{
serverAddress.PathBase = address.Substring(pathDelimiterEnd);
}
return serverAddress;
}
}
}

View File

@ -7,6 +7,7 @@ namespace Microsoft.AspNetCore.Http.Internal
{
internal const string Http = "http";
internal const string Https = "https";
internal const string UnixPipeHostPrefix = "unix:/";
internal static class BuilderProperties
{

View File

@ -0,0 +1,70 @@
// 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.Collections.Generic;
using System.Text;
using Xunit;
namespace Microsoft.AspNetCore.Http.Internal.Tests
{
public class BindingAddressTests
{
[Theory]
[InlineData("")]
[InlineData("5000")]
[InlineData("//noscheme")]
public void FromUriThrowsForUrlsWithoutSchemeDelimiter(string url)
{
Assert.Throws<FormatException>(() => BindingAddress.Parse(url));
}
[Theory]
[InlineData("://")]
[InlineData("://:5000")]
[InlineData("http://")]
[InlineData("http://:5000")]
[InlineData("http:///")]
[InlineData("http:///:5000")]
[InlineData("http:////")]
[InlineData("http:////:5000")]
public void FromUriThrowsForUrlsWithoutHost(string url)
{
Assert.Throws<FormatException>(() => BindingAddress.Parse(url));
}
[Theory]
[InlineData("://emptyscheme", "", "emptyscheme", 0, "", "://emptyscheme:0")]
[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, "", null)]
[InlineData("https://www.example.com:5000", "https", "www.example.com", 5000, "", null)]
[InlineData("http://www.example.com:5000/", "http", "www.example.com", 5000, "", "http://www.example.com:5000")]
[InlineData("http://www.example.com:NOTAPORT", "http", "www.example.com:NOTAPORT", 80, "", "http://www.example.com:notaport:80")]
[InlineData("https://www.example.com:NOTAPORT", "https", "www.example.com:NOTAPORT", 443, "", "https://www.example.com:notaport:443")]
[InlineData("http://www.example.com:NOTAPORT/", "http", "www.example.com:NOTAPORT", 80, "", "http://www.example.com:notaport:80")]
[InlineData("http://foo:/tmp/kestrel-test.sock:5000/doesn't/matter", "http", "foo:", 80, "/tmp/kestrel-test.sock:5000/doesn't/matter", "http://foo::80/tmp/kestrel-test.sock:5000/doesn't/matter")]
[InlineData("http://unix:foo/tmp/kestrel-test.sock", "http", "unix:foo", 80, "/tmp/kestrel-test.sock", "http://unix:foo:80/tmp/kestrel-test.sock")]
[InlineData("http://unix:5000/tmp/kestrel-test.sock", "http", "unix", 5000, "/tmp/kestrel-test.sock", "http://unix:5000/tmp/kestrel-test.sock")]
[InlineData("http://unix:/tmp/kestrel-test.sock", "http", "unix:/tmp/kestrel-test.sock", 0, "", null)]
[InlineData("https://unix:/tmp/kestrel-test.sock", "https", "unix:/tmp/kestrel-test.sock", 0, "", null)]
[InlineData("http://unix:/tmp/kestrel-test.sock:", "http", "unix:/tmp/kestrel-test.sock", 0, "", "http://unix:/tmp/kestrel-test.sock")]
[InlineData("http://unix:/tmp/kestrel-test.sock:/", "http", "unix:/tmp/kestrel-test.sock", 0, "", "http://unix:/tmp/kestrel-test.sock")]
[InlineData("http://unix:/tmp/kestrel-test.sock:5000/doesn't/matter", "http", "unix:/tmp/kestrel-test.sock", 0, "5000/doesn't/matter", "http://unix:/tmp/kestrel-test.sock")]
public void UrlsAreParsedCorrectly(string url, string scheme, string host, int port, string pathBase, string toString)
{
var serverAddress = BindingAddress.Parse(url);
Assert.Equal(scheme, serverAddress.Scheme);
Assert.Equal(host, serverAddress.Host);
Assert.Equal(port, serverAddress.Port);
Assert.Equal(pathBase, serverAddress.PathBase);
Assert.Equal(toString ?? url, serverAddress.ToString());
}
}
}