Example #1
0
		/// <summary>
		/// Finds the domain that best matches the current uri, into an enumeration of domains.
		/// </summary>
		/// <param name="domains">The enumeration of Umbraco domains.</param>
		/// <param name="current">The uri of the current request, or null.</param>
		/// <param name="defaultToFirst">A value indicating whether to return the first domain of the list when no domain matches.</param>
		/// <returns>The domain and its normalized uri, that best matches the current uri, else the first domain (if <c>defaultToFirst</c> is <c>true</c>), else null.</returns>
		public static DomainAndUri DomainMatch(IEnumerable<Domain> domains, Uri current, bool defaultToFirst)
		{
			if (!domains.Any())
				return null;

			// sanitize the list to have proper uris for comparison (scheme, path end with /)
			// we need to end with / because example.com/foo cannot match example.com/foobar
			// we need to order so example.com/foo matches before example.com/
			var scheme = current == null ? Uri.UriSchemeHttp : current.Scheme;
			var domainsAndUris = domains
				.Select(d => new { Domain = d, UriString = UriUtility.EndPathWithSlash(UriUtility.StartWithScheme(d.Name, scheme)) })
				.OrderByDescending(t => t.UriString)
				.Select(t => new DomainAndUri { Domain = t.Domain, Uri = new Uri(t.UriString) });

			DomainAndUri domainAndUri;
			if (current == null)
			{
				// take the first one by default
				domainAndUri = domainsAndUris.First();
			}
			else
			{
				// look for a domain that would be the base of the hint
				// else take the first one by default
				var hintWithSlash = current.EndPathWithSlash();
				domainAndUri = domainsAndUris
					.FirstOrDefault(t => t.Uri.IsBaseOf(hintWithSlash));
				if (domainAndUri == null && defaultToFirst)
					domainAndUri = domainsAndUris.First();
			}

			if (domainAndUri != null)
				domainAndUri.Uri = domainAndUri.Uri.TrimPathEndSlash();
			return domainAndUri;
		}
 	    /// <summary>
 	    /// Filters a list of <c>DomainAndUri</c> to pick those that best matches the current request.
 	    /// </summary>
 	    /// <param name="current">The Uri of the current request.</param>
 	    /// <param name="domainAndUris">The list of <c>DomainAndUri</c> to filter.</param>
        /// <param name="excludeDefault">A value indicating whether to exclude the current/default domain.</param>
 	    /// <returns>The selected <c>DomainAndUri</c> items.</returns>
 	    /// <remarks>The filter must return something, even empty, else an exception will be thrown.</remarks>
 	    public virtual IEnumerable<DomainAndUri> MapDomains(Uri current, DomainAndUri[] domainAndUris, bool excludeDefault)
        {
            var currentAuthority = current.GetLeftPart(UriPartial.Authority);
            KeyValuePair<string, string[]>[] candidateSites = null;
 	        IEnumerable<DomainAndUri> ret = domainAndUris;

            using (ConfigReadLock) // so nothing changes between GetQualifiedSites and access to bindings
            {
                var qualifiedSites = GetQualifiedSitesInsideLock(current);

                if (excludeDefault)
                {
                    // exclude the current one (avoid producing the absolute equivalent of what GetUrl returns)
                    var hintWithSlash = current.EndPathWithSlash();
                    var hinted = domainAndUris.FirstOrDefault(d => d.Uri.EndPathWithSlash().IsBaseOf(hintWithSlash));
                    if (hinted != null)
                        ret = ret.Where(d => d != hinted);

                    // exclude the default one (avoid producing a possible duplicate of what GetUrl returns)
                    // only if the default one cannot be the current one ie if hinted is not null
                    if (hinted == null && domainAndUris.Any())
                    {
                        // it is illegal to call MapDomain if domainAndUris is empty
                        // also, domainAndUris should NOT contain current, hence the test on hinted
                        var mainDomain = MapDomain(domainAndUris, qualifiedSites, currentAuthority); // what GetUrl would get
                        ret = ret.Where(d => d != mainDomain);
                    }
                }

                // we do our best, but can't do the impossible
                if (qualifiedSites == null)
                    return ret;

                // find a site that contains the current authority
                var currentSite = qualifiedSites.FirstOrDefault(site => site.Value.Contains(currentAuthority));

                // if current belongs to a site, pick every element from domainAndUris that also belong
                // to that site -- or to any site bound to that site

                if (!currentSite.Equals(default(KeyValuePair<string, string[]>)))
                {
                    candidateSites = new[] { currentSite };
                    if (_bindings != null && _bindings.ContainsKey(currentSite.Key))
                    {
                        var boundSites = qualifiedSites.Where(site => _bindings[currentSite.Key].Contains(site.Key));
                        candidateSites = candidateSites.Union(boundSites).ToArray();

                        // .ToArray ensures it is evaluated before the configuration lock is exited
                    }
                }
            }
 
            // if we are able to filter, then filter, else return the whole lot
            return candidateSites == null ? ret : ret.Where(d =>
                {
                    var authority = d.Uri.GetLeftPart(UriPartial.Authority);
                    return candidateSites.Any(site => site.Value.Contains(authority));
                });
         }
 public void EndPathWithSlash(string input, string expected)
 {
     var source = new Uri(input);
     var output = source.EndPathWithSlash();
     Assert.AreEqual(expected, output.ToString());
 }
Example #4
0
        /// <summary>
        /// Finds the domain that best matches a specified uri, into a group of domains.
        /// </summary>
        /// <param name="domains">The group of domains.</param>
        /// <param name="current">The uri, or null.</param>
        /// <param name="filter">A function to filter the list of domains, if more than one applies, or <c>null</c>.</param>
        /// <returns>The domain and its normalized uri, that best matches the specified uri.</returns>
        /// <remarks>
        /// <para>If more than one domain matches, then the <paramref name="filter"/> function is used to pick
        /// the right one, unless it is <c>null</c>, in which case the method returns <c>null</c>.</para>
        /// <para>The filter, if any, will be called only with a non-empty argument, and _must_ return something.</para>
        /// </remarks>
        internal static DomainAndUri DomainForUri(Domain[] domains, Uri current, Func<DomainAndUri[], DomainAndUri> filter = null)
        {
            // sanitize the list to have proper uris for comparison (scheme, path end with /)
            // we need to end with / because example.com/foo cannot match example.com/foobar
            // we need to order so example.com/foo matches before example.com/
            var scheme = current == null ? Uri.UriSchemeHttp : current.Scheme;
            var domainsAndUris = domains
                .Where(d => !d.IsWildcard)
                .Select(SanitizeForBackwardCompatibility)
                .Select(d => new DomainAndUri(d, scheme))
                .OrderByDescending(d => d.Uri.ToString())
                .ToArray();

            if (!domainsAndUris.Any())
                return null;

            DomainAndUri domainAndUri;
            if (current == null)
            {
                // take the first one by default (what else can we do?)
                domainAndUri = domainsAndUris.First(); // .First() protected by .Any() above
            }
            else
            {
                // look for the first domain that would be the base of the hint
                var hintWithSlash = current.EndPathWithSlash();
                domainAndUri = domainsAndUris
                    .FirstOrDefault(d => d.Uri.EndPathWithSlash().IsBaseOf(hintWithSlash));
                // if none matches, then try to run the filter to pick a domain
                if (domainAndUri == null && filter != null)
                {
                    domainAndUri = filter(domainsAndUris);
                    // if still nothing, pick the first one?
                    // no: move that constraint to the filter, but check
                    if (domainAndUri == null)
                        throw new InvalidOperationException("The filter returned null.");
                }
            }

            return domainAndUri;
        }