// Copyright (c) Microsoft Open Technologies, Inc. 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.CodeAnalysis; using System.Globalization; namespace Microsoft.AspNet.Http { /// /// Represents the host portion of a URI can be used to construct URI's properly formatted and encoded for use in /// HTTP headers. /// public struct HostString : IEquatable { private readonly string _value; /// /// Creates a new HostString without modification. The value should be Unicode rather than punycode, and may have a port. /// IPv4 and IPv6 addresses are also allowed, and also may have ports. /// /// public HostString(string value) { _value = value; } /// /// Returns the original value from the constructor. /// public string Value { get { return _value; } } public bool HasValue { get { return !string.IsNullOrEmpty(_value); } } /// /// Returns the value as normalized by ToUriComponent(). /// /// public override string ToString() { return ToUriComponent(); } /// /// Returns the value properly formatted and encoded for use in a URI in a HTTP header. /// Any Unicode is converted to punycode. IPv6 addresses will have brackets added if they are missing. /// /// [SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "Only the host segment of a URI is returned.")] public string ToUriComponent() { int index; if (string.IsNullOrEmpty(_value)) { return string.Empty; } else if (_value.IndexOf('[') >= 0) { // IPv6 in brackets [::1], maybe with port return _value; } else if ((index = _value.IndexOf(':')) >= 0 && index < _value.Length - 1 && _value.IndexOf(':', index + 1) >= 0) { // IPv6 without brackets ::1 is the only type of host with 2 or more colons return "[" + _value + "]"; } else if (index >= 0) { // Has a port string port = _value.Substring(index); IdnMapping mapping = new IdnMapping(); return mapping.GetAscii(_value, 0, index) + port; } else { IdnMapping mapping = new IdnMapping(); return mapping.GetAscii(_value); } } /// /// Creates a new HostString from the given URI component. /// Any punycode will be converted to Unicode. /// /// /// [SuppressMessage("Microsoft.Design", "CA1057:StringUriOverloadsCallSystemUriOverloads", Justification = "Only the host segment of a URI is provided.")] public static HostString FromUriComponent(string uriComponent) { if (!string.IsNullOrEmpty(uriComponent)) { int index; if (uriComponent.IndexOf('[') >= 0) { // IPv6 in brackets [::1], maybe with port } else if ((index = uriComponent.IndexOf(':')) >= 0 && index < uriComponent.Length - 1 && uriComponent.IndexOf(':', index + 1) >= 0) { // IPv6 without brackets ::1 is the only type of host with 2 or more colons } else if (uriComponent.IndexOf("xn--", StringComparison.Ordinal) >= 0) { // Contains punycode if (index >= 0) { // Has a port string port = uriComponent.Substring(index); IdnMapping mapping = new IdnMapping(); uriComponent = mapping.GetUnicode(uriComponent, 0, index) + port; } else { IdnMapping mapping = new IdnMapping(); uriComponent = mapping.GetUnicode(uriComponent); } } } return new HostString(uriComponent); } /// /// Creates a new HostString from the host and port of the give Uri instance. /// Punycode will be converted to Unicode. /// /// /// public static HostString FromUriComponent(Uri uri) { if (uri == null) { throw new ArgumentNullException("uri"); } return new HostString(uri.GetComponents( UriComponents.NormalizedHost | // Always convert punycode to Unicode. UriComponents.HostAndPort, UriFormat.Unescaped)); } /// /// Compares the equality of the Value property, ignoring case. /// /// /// public bool Equals(HostString other) { return string.Equals(_value, other._value, StringComparison.OrdinalIgnoreCase); } /// /// Compares against the given object only if it is a HostString. /// /// /// public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) { return false; } return obj is HostString && Equals((HostString)obj); } /// /// Gets a hash code for the value. /// /// public override int GetHashCode() { return (_value != null ? StringComparer.OrdinalIgnoreCase.GetHashCode(_value) : 0); } /// /// Compares the two instances for equality. /// /// /// /// public static bool operator ==(HostString left, HostString right) { return left.Equals(right); } /// /// Compares the two instances for inequality. /// /// /// /// public static bool operator !=(HostString left, HostString right) { return !left.Equals(right); } } }