Addresses #678 and #679

This commit is contained in:
Justin Kotalik 2016-08-03 14:38:54 -07:00 committed by Justin Kotalik
parent e320755734
commit a4a4e490c5
8 changed files with 250 additions and 8 deletions

View File

@ -107,21 +107,25 @@ namespace Microsoft.AspNetCore.Http
public bool Equals(FragmentString other)
{
return string.Equals(_value, other._value);
if (!HasValue && !other.HasValue)
{
return true;
}
return string.Equals(_value, other._value, StringComparison.Ordinal);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
return !HasValue;
}
return obj is FragmentString && Equals((FragmentString)obj);
}
public override int GetHashCode()
{
return (_value != null ? _value.GetHashCode() : 0);
return (HasValue ? _value.GetHashCode() : 0);
}
public static bool operator ==(FragmentString left, FragmentString right)

View File

@ -205,6 +205,10 @@ namespace Microsoft.AspNetCore.Http
/// <returns></returns>
public bool Equals(HostString other)
{
if (!HasValue && !other.HasValue)
{
return true;
}
return string.Equals(_value, other._value, StringComparison.OrdinalIgnoreCase);
}
@ -217,7 +221,7 @@ namespace Microsoft.AspNetCore.Http
{
if (ReferenceEquals(null, obj))
{
return false;
return !HasValue;
}
return obj is HostString && Equals((HostString)obj);
}
@ -228,7 +232,7 @@ namespace Microsoft.AspNetCore.Http
/// <returns></returns>
public override int GetHashCode()
{
return (_value != null ? StringComparer.OrdinalIgnoreCase.GetHashCode(_value) : 0);
return (HasValue ? StringComparer.OrdinalIgnoreCase.GetHashCode(_value) : 0);
}
/// <summary>

View File

@ -215,21 +215,25 @@ namespace Microsoft.AspNetCore.Http
public bool Equals(QueryString other)
{
return string.Equals(_value, other._value);
if (!HasValue && !other.HasValue)
{
return true;
}
return string.Equals(_value, other._value, StringComparison.Ordinal);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
return !HasValue;
}
return obj is QueryString && Equals((QueryString)obj);
}
public override int GetHashCode()
{
return _value?.GetHashCode() ?? 0;
return (HasValue ? _value.GetHashCode() : 0);
}
public static bool operator ==(QueryString left, QueryString right)

View File

@ -11,6 +11,9 @@ namespace Microsoft.AspNetCore.Http.Extensions
/// </summary>
public static class UriHelper
{
private const string ForwardSlash = "/";
private const string Pound = "#";
private const string QuestionMark = "?";
private const string SchemeDelimiter = "://";
/// <summary>
@ -70,6 +73,67 @@ namespace Microsoft.AspNetCore.Http.Extensions
.ToString();
}
/// <summary>
/// Seperates the given absolute URI string into components. Assumes no PathBase.
/// </summary>
/// <param name="uri">A string representation of the uri.</param>
/// <param name="scheme">http, https, etc.</param>
/// <param name="host">The host portion of the uri normally included in the Host header. This may include the port.</param>
/// <param name="path">The portion of the request path that identifies the requested resource.</param>
/// <param name="query">The query, if any.</param>
/// <param name="fragment">The fragment, if any.</param>
public static void FromAbsolute(
string uri,
out string scheme,
out HostString host,
out PathString path,
out QueryString query,
out FragmentString fragment)
{
if (uri == null)
{
throw new ArgumentNullException(nameof(uri));
}
// Satisfy the out parameters
path = new PathString();
query = new QueryString();
fragment = new FragmentString();
var startIndex = uri.IndexOf(SchemeDelimiter);
if (startIndex < 0)
{
throw new FormatException("No scheme delimiter in uri.");
}
scheme = uri.Substring(0, startIndex);
// PERF: Calculate the end of the scheme for next IndexOf
startIndex += SchemeDelimiter.Length;
var searchIndex = -1;
var limit = uri.Length;
if ((searchIndex = uri.IndexOf(Pound, startIndex)) >= 0 && searchIndex < limit)
{
fragment = FragmentString.FromUriComponent(uri.Substring(searchIndex));
limit = searchIndex;
}
if ((searchIndex = uri.IndexOf(QuestionMark, startIndex)) >= 0 && searchIndex < limit)
{
query = QueryString.FromUriComponent(uri.Substring(searchIndex, limit - searchIndex));
limit = searchIndex;
}
if ((searchIndex = uri.IndexOf(ForwardSlash, startIndex)) >= 0 && searchIndex < limit)
{
path = PathString.FromUriComponent(uri.Substring(searchIndex, limit - searchIndex));
limit = searchIndex;
}
host = HostString.FromUriComponent(uri.Substring(startIndex, limit - startIndex));
}
/// <summary>
/// Generates a string from the given absolute or relative Uri that is appropriately encoded for use in
/// HTTP headers. Note that a unicode host name will be encoded as punycode.

View File

@ -0,0 +1,41 @@
// 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 Xunit;
namespace Microsoft.AspNetCore.Http.Abstractions.Tests
{
public class FragmentStringTests
{
[Fact]
public void Equals_EmptyFragmentStringAndDefaultFragmentString()
{
// Act and Assert
Assert.Equal(FragmentString.Empty, default(FragmentString));
Assert.Equal(default(FragmentString), FragmentString.Empty);
// explicitly checking == operator
Assert.True(FragmentString.Empty == default(FragmentString));
Assert.True(default(FragmentString) == FragmentString.Empty);
}
[Fact]
public void NotEquals_DefaultFragmentStringAndNonNullFragmentString()
{
// Arrange
var fragmentString = new FragmentString("#col=1");
// Act and Assert
Assert.NotEqual(fragmentString, default(FragmentString));
}
[Fact]
public void NotEquals_EmptyFragmentStringAndNonNullFragmentString()
{
// Arrange
var fragmentString = new FragmentString("#col=1");
// Act and Assert
Assert.NotEqual(fragmentString, FragmentString.Empty);
}
}
}

View File

@ -92,5 +92,36 @@ namespace Microsoft.AspNetCore.Http
Assert.Equal(expectedHost, host);
Assert.Equal(expectedPort, port);
}
[Fact]
public void Equals_EmptyHostStringAndDefaultHostString()
{
// Act and Assert
Assert.Equal(new HostString(string.Empty), default(HostString));
Assert.Equal(default(HostString), new HostString(string.Empty));
// explicitly checking == operator
Assert.True(new HostString(string.Empty) == default(HostString));
Assert.True(default(HostString) == new HostString(string.Empty));
}
[Fact]
public void NotEquals_DefaultHostStringAndNonNullHostString()
{
// Arrange
var hostString = new HostString("example.com");
// Act and Assert
Assert.NotEqual(hostString, default(HostString));
}
[Fact]
public void NotEquals_EmptyHostStringAndNonNullHostString()
{
// Arrange
var hostString = new HostString("example.com");
// Act and Assert
Assert.NotEqual(hostString, new HostString(string.Empty));
}
}
}

View File

@ -105,5 +105,36 @@ namespace Microsoft.AspNetCore.Http.Abstractions
var q2 = q1.Add(name2, value2);
Assert.Equal(expected, q2.Value);
}
[Fact]
public void Equals_EmptyQueryStringAndDefaultQueryString()
{
// Act and Assert
Assert.Equal(QueryString.Empty, default(QueryString));
Assert.Equal(default(QueryString), QueryString.Empty);
// explicitly checking == operator
Assert.True(QueryString.Empty == default(QueryString));
Assert.True(default(QueryString) == QueryString.Empty);
}
[Fact]
public void NotEquals_DefaultQueryStringAndNonNullQueryString()
{
// Arrange
var queryString = new QueryString("?foo=1");
// Act and Assert
Assert.NotEqual(queryString, default(QueryString));
}
[Fact]
public void NotEquals_EmptyQueryStringAndNonNullQueryString()
{
// Arrange
var queryString = new QueryString("?foo=1");
// Act and Assert
Assert.NotEqual(queryString, QueryString.Empty);
}
}
}

View File

@ -66,5 +66,68 @@ namespace Microsoft.AspNetCore.Http.Extensions
Assert.Equal("http://my.hoψst:80/un?escaped/base/un?escaped?name=val%23ue", request.GetDisplayUrl());
}
[Theory]
[InlineData("http://example.com", "http", "example.com", "", "", "")]
[InlineData("https://example.com", "https", "example.com", "", "", "")]
[InlineData("http://example.com/foo/bar", "http", "example.com", "/foo/bar", "", "")]
[InlineData("http://example.com/foo/bar?baz=1", "http", "example.com", "/foo/bar", "?baz=1", "")]
[InlineData("http://example.com/foo#col=2", "http", "example.com", "/foo", "", "#col=2")]
[InlineData("http://example.com/foo?bar=1#col=2", "http", "example.com", "/foo", "?bar=1", "#col=2")]
[InlineData("http://example.com?bar=1#col=2", "http", "example.com", "", "?bar=1", "#col=2")]
[InlineData("http://example.com#frag?stillfrag/stillfrag", "http", "example.com", "", "", "#frag?stillfrag/stillfrag")]
[InlineData("http://example.com?q/stillq#frag?stillfrag/stillfrag", "http", "example.com", "", "?q/stillq", "#frag?stillfrag/stillfrag")]
[InlineData("http://example.com/fo%23o#col=2", "http", "example.com", "/fo#o", "", "#col=2")]
[InlineData("http://example.com/fo%3Fo#col=2", "http", "example.com", "/fo?o", "", "#col=2")]
[InlineData("ftp://example.com/", "ftp", "example.com", "/", "", "")]
[InlineData("https://127.0.0.0:80/bar", "https", "127.0.0.0:80", "/bar", "", "")]
[InlineData("http://[1080:0:0:0:8:800:200C:417A]/index.html", "http", "[1080:0:0:0:8:800:200C:417A]", "/index.html", "", "")]
[InlineData("http://example.com///", "http", "example.com", "///", "", "")]
[InlineData("http://example.com///", "http", "example.com", "///", "", "")]
public void FromAbsoluteUriParsingChecks(
string uri,
string expectedScheme,
string expectedHost,
string expectedPath,
string expectedQuery,
string expectedFragment)
{
string scheme = null;
var host = new HostString();
var path = new PathString();
var query = new QueryString();
var fragment = new FragmentString();
UriHelper.FromAbsolute(uri, out scheme, out host, out path, out query, out fragment);
Assert.Equal(scheme, expectedScheme);
Assert.Equal(host, new HostString(expectedHost));
Assert.Equal(path, new PathString(expectedPath));
Assert.Equal(query, new QueryString(expectedQuery));
Assert.Equal(fragment, new FragmentString(expectedFragment));
}
[Fact]
public void FromAbsoluteToBuildAbsolute()
{
var scheme = "http";
var host = new HostString("example.com");
var path = new PathString("/index.html");
var query = new QueryString("?foo=1");
var fragment = new FragmentString("#col=1");
var request = UriHelper.BuildAbsolute(scheme, host, path:path, query:query, fragment:fragment);
string resScheme = null;
var resHost = new HostString();
var resPath = new PathString();
var resQuery = new QueryString();
var resFragment = new FragmentString();
UriHelper.FromAbsolute(request, out resScheme, out resHost, out resPath, out resQuery, out resFragment);
Assert.Equal(scheme, resScheme);
Assert.Equal(host, resHost);
Assert.Equal(path, resPath);
Assert.Equal(query, resQuery);
Assert.Equal(fragment, resFragment);
}
}
}