Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Adding RouteDebugger to this solution.

  • Loading branch information...
commit 8819e497eae48f34d97892cbb0e3d034276d5e2d 1 parent c7a8a72
haacked authored
180 src/RouteDebug/DebugHttpHandler.cs
... ... @@ -0,0 +1,180 @@
  1 +using System.Web;
  2 +using System.Web.Routing;
  3 +
  4 +namespace RouteDebug
  5 +{
  6 + public class DebugHttpHandler : IHttpHandler
  7 + {
  8 + public RequestContext RequestContext { get; set; }
  9 +
  10 + public bool IsReusable
  11 + {
  12 + get { return true; }
  13 + }
  14 +
  15 + public void ProcessRequest(HttpContext context)
  16 + {
  17 + string generatedUrlInfo = string.Empty;
  18 + if (context.Request.QueryString.Count > 0) {
  19 + var rvalues = new RouteValueDictionary();
  20 + foreach(string key in context.Request.QueryString.Keys) {
  21 + rvalues.Add(key, context.Request.QueryString[key]);
  22 + }
  23 +
  24 + var vpd = RouteTable.Routes.GetVirtualPath(RequestContext, rvalues);
  25 + if (vpd != null) {
  26 + generatedUrlInfo = "<p><label>Generated URL</label>: ";
  27 + generatedUrlInfo += "<strong style=\"color: #00a;\">" + vpd.VirtualPath + "</strong>";
  28 + var vpdRoute = vpd.Route as Route;
  29 + if (vpdRoute != null) {
  30 + generatedUrlInfo += " using the route \"" + vpdRoute.Url + "\"</p>";
  31 + }
  32 + }
  33 + }
  34 +
  35 + string htmlFormat = @"<html>
  36 +<head>
  37 + <title>Route Tester</title>
  38 + <style>
  39 + body, td, th {{font-family: verdana; font-size: small;}}
  40 + .message {{font-size: .9em;}}
  41 + caption {{font-weight: bold;}}
  42 + tr.header {{background-color: #ffc;}}
  43 + label {{font-weight: bold; font-size: 1.1em;}}
  44 + .false {{color: #c00;}}
  45 + .true {{color: #0c0;}}
  46 + </style>
  47 +</head>
  48 +<body>
  49 +<h1>Route Tester</h1>
  50 +<div id=""main"">
  51 + <p class=""message"">
  52 + Type in a url in the address bar to see which defined routes match it.
  53 + A {{*catchall}} route is added to the list of routes automatically in
  54 + case none of your routes match.
  55 + </p>
  56 + <p class=""message"">
  57 + To generate URLs using routing, supply route values via the query string. example: <code>http://localhost:14230/?id=123</code>
  58 + </p>
  59 + <p><label>Matched Route</label>: {1}</p>
  60 + {5}
  61 + <div style=""float: left;"">
  62 + <table border=""1"" cellpadding=""3"" cellspacing=""0"" width=""300"">
  63 + <caption>Route Data</caption>
  64 + <tr class=""header""><th>Key</th><th>Value</th></tr>
  65 + {0}
  66 + </table>
  67 + </div>
  68 + <div style=""float: left; margin-left: 10px;"">
  69 + <table border=""1"" cellpadding=""3"" cellspacing=""0"" width=""300"">
  70 + <caption>Data Tokens</caption>
  71 + <tr class=""header""><th>Key</th><th>Value</th></tr>
  72 + {4}
  73 + </table>
  74 + </div>
  75 + <hr style=""clear: both;"" />
  76 + <table border=""1"" cellpadding=""3"" cellspacing=""0"">
  77 + <caption>All Routes</caption>
  78 + <tr class=""header"">
  79 + <th>Matches Current Request</th>
  80 + <th>Url</th>
  81 + <th>Defaults</th>
  82 + <th>Constraints</th>
  83 + <th>DataTokens</th>
  84 + </tr>
  85 + {2}
  86 + </table>
  87 + <hr />
  88 + <h3>Current Request Info</h3>
  89 + <p>
  90 + AppRelativeCurrentExecutionFilePath is the portion of the request that Routing acts on.
  91 + </p>
  92 + <p><strong>AppRelativeCurrentExecutionFilePath</strong>: {3}</p>
  93 +</div>
  94 +</body>
  95 +</html>";
  96 + string routeDataRows = string.Empty;
  97 +
  98 + RouteData routeData = this.RequestContext.RouteData;
  99 + RouteValueDictionary routeValues = routeData.Values;
  100 + RouteBase matchedRouteBase = routeData.Route;
  101 +
  102 + string routes = string.Empty;
  103 + using (RouteTable.Routes.GetReadLock())
  104 + {
  105 + foreach (RouteBase routeBase in RouteTable.Routes)
  106 + {
  107 + bool matchesCurrentRequest = (routeBase.GetRouteData(RequestContext.HttpContext) != null);
  108 + string matchText = string.Format(@"<span class=""{0}"">{0}</span>", matchesCurrentRequest);
  109 + string url = "n/a";
  110 + string defaults = "n/a";
  111 + string constraints = "n/a";
  112 + string dataTokens = "n/a";
  113 +
  114 + Route route = routeBase as Route;
  115 + if (route != null)
  116 + {
  117 + url = route.Url;
  118 + defaults = FormatRouteValueDictionary(route.Defaults);
  119 + constraints = FormatRouteValueDictionary(route.Constraints);
  120 + dataTokens = FormatRouteValueDictionary(route.DataTokens);
  121 + }
  122 +
  123 + routes += string.Format(@"<tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3}</td><td>{4}</td></tr>"
  124 + , matchText
  125 + , url
  126 + , defaults
  127 + , constraints
  128 + , dataTokens);
  129 + }
  130 + }
  131 +
  132 + string matchedRouteUrl = "n/a";
  133 +
  134 + string dataTokensRows = "";
  135 +
  136 + if (!(matchedRouteBase is DebugRoute))
  137 + {
  138 + foreach (string key in routeValues.Keys)
  139 + {
  140 + routeDataRows += string.Format("\t<tr><td>{0}</td><td>{1}&nbsp;</td></tr>", key, routeValues[key]);
  141 + }
  142 +
  143 + foreach (string key in routeData.DataTokens.Keys)
  144 + {
  145 + dataTokensRows += string.Format("\t<tr><td>{0}</td><td>{1}&nbsp;</td></tr>", key, routeData.DataTokens[key]);
  146 + }
  147 +
  148 + Route matchedRoute = matchedRouteBase as Route;
  149 +
  150 + if (matchedRoute != null)
  151 + matchedRouteUrl = matchedRoute.Url;
  152 + }
  153 + else
  154 + {
  155 + matchedRouteUrl = "<strong class=\"false\">NO MATCH!</strong>";
  156 + }
  157 +
  158 + context.Response.Write(string.Format(htmlFormat
  159 + , routeDataRows
  160 + , matchedRouteUrl
  161 + , routes
  162 + , context.Request.AppRelativeCurrentExecutionFilePath
  163 + , dataTokensRows
  164 + , generatedUrlInfo));
  165 + }
  166 +
  167 + private static string FormatRouteValueDictionary(RouteValueDictionary values)
  168 + {
  169 + if (values == null || values.Count == 0)
  170 + return "(null)";
  171 +
  172 + string display = string.Empty;
  173 + foreach (string key in values.Keys)
  174 + display += string.Format("{0} = {1}, ", key, values[key]);
  175 + if (display.EndsWith(", "))
  176 + display = display.Substring(0, display.Length - 2);
  177 + return display;
  178 + }
  179 + }
  180 +}
18 src/RouteDebug/DebugRoute.cs
... ... @@ -0,0 +1,18 @@
  1 +using System.Web.Routing;
  2 +
  3 +namespace RouteDebug
  4 +{
  5 + public class DebugRoute : Route
  6 + {
  7 + private static DebugRoute singleton = new DebugRoute();
  8 +
  9 + public static DebugRoute Singleton
  10 + {
  11 + get { return singleton; }
  12 + }
  13 +
  14 + private DebugRoute()
  15 + : base("{*catchall}", new DebugRouteHandler())
  16 + { }
  17 + }
  18 +}
13 src/RouteDebug/DebugRouteHandler.cs
... ... @@ -0,0 +1,13 @@
  1 +using System.Web;
  2 +using System.Web.Routing;
  3 +
  4 +namespace RouteDebug
  5 +{
  6 + public class DebugRouteHandler : IRouteHandler
  7 + {
  8 + public IHttpHandler GetHttpHandler(RequestContext requestContext)
  9 + {
  10 + return new DebugHttpHandler { RequestContext = requestContext };
  11 + }
  12 + }
  13 +}
36 src/RouteDebug/Properties/AssemblyInfo.cs
... ... @@ -0,0 +1,36 @@
  1 +using System.Reflection;
  2 +using System.Runtime.CompilerServices;
  3 +using System.Runtime.InteropServices;
  4 +
  5 +// General Information about an assembly is controlled through the following
  6 +// set of attributes. Change these attribute values to modify the information
  7 +// associated with an assembly.
  8 +[assembly: AssemblyTitle("RouteDebugger")]
  9 +[assembly: AssemblyDescription("Route Debugger is a little utility I wrote to help debug issues with route configurations. See http://haacked.com/archive/2008/03/13/url-routing-debugger.aspx")]
  10 +[assembly: AssemblyConfiguration("")]
  11 +[assembly: AssemblyCompany("")]
  12 +[assembly: AssemblyProduct("RouteDebugger")]
  13 +[assembly: AssemblyCopyright("Copyright © 2008")]
  14 +[assembly: AssemblyTrademark("")]
  15 +[assembly: AssemblyCulture("")]
  16 +
  17 +// Setting ComVisible to false makes the types in this assembly not visible
  18 +// to COM components. If you need to access a type in this assembly from
  19 +// COM, set the ComVisible attribute to true on that type.
  20 +[assembly: ComVisible(false)]
  21 +
  22 +// The following GUID is for the ID of the typelib if this project is exposed to COM
  23 +[assembly: Guid("88784f4d-3799-4a7a-8e1d-16bbfbd77416")]
  24 +
  25 +// Version information for an assembly consists of the following four values:
  26 +//
  27 +// Major Version
  28 +// Minor Version
  29 +// Build Number
  30 +// Revision
  31 +//
  32 +// You can specify all the values or you can default the Build and Revision Numbers
  33 +// by using the '*' as shown below:
  34 +// [assembly: AssemblyVersion("1.0.*")]
  35 +[assembly: AssemblyVersion("1.0.0.0")]
  36 +[assembly: AssemblyFileVersion("1.0.0.0")]
112 src/RouteDebug/RouteDebug.csproj
... ... @@ -0,0 +1,112 @@
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  3 + <PropertyGroup>
  4 + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
  5 + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
  6 + <ProductVersion>9.0.30729</ProductVersion>
  7 + <SchemaVersion>2.0</SchemaVersion>
  8 + <ProjectGuid>{09548C4E-F0C1-428A-BC3A-BFBB9328D7C7}</ProjectGuid>
  9 + <OutputType>Library</OutputType>
  10 + <AppDesignerFolder>Properties</AppDesignerFolder>
  11 + <RootNamespace>RouteDebug</RootNamespace>
  12 + <AssemblyName>RouteDebug</AssemblyName>
  13 + <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
  14 + <FileAlignment>512</FileAlignment>
  15 + <FileUpgradeFlags>
  16 + </FileUpgradeFlags>
  17 + <UpgradeBackupLocation>
  18 + </UpgradeBackupLocation>
  19 + <OldToolsVersion>3.5</OldToolsVersion>
  20 + <TargetFrameworkProfile />
  21 + <PublishUrl>publish\</PublishUrl>
  22 + <Install>true</Install>
  23 + <InstallFrom>Disk</InstallFrom>
  24 + <UpdateEnabled>false</UpdateEnabled>
  25 + <UpdateMode>Foreground</UpdateMode>
  26 + <UpdateInterval>7</UpdateInterval>
  27 + <UpdateIntervalUnits>Days</UpdateIntervalUnits>
  28 + <UpdatePeriodically>false</UpdatePeriodically>
  29 + <UpdateRequired>false</UpdateRequired>
  30 + <MapFileExtensions>true</MapFileExtensions>
  31 + <ApplicationRevision>0</ApplicationRevision>
  32 + <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
  33 + <IsWebBootstrapper>false</IsWebBootstrapper>
  34 + <UseApplicationTrust>false</UseApplicationTrust>
  35 + <BootstrapperEnabled>true</BootstrapperEnabled>
  36 + </PropertyGroup>
  37 + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
  38 + <DebugSymbols>true</DebugSymbols>
  39 + <DebugType>full</DebugType>
  40 + <Optimize>false</Optimize>
  41 + <OutputPath>bin\Debug\</OutputPath>
  42 + <DefineConstants>DEBUG;TRACE</DefineConstants>
  43 + <ErrorReport>prompt</ErrorReport>
  44 + <WarningLevel>4</WarningLevel>
  45 + <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
  46 + <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
  47 + </PropertyGroup>
  48 + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
  49 + <DebugType>pdbonly</DebugType>
  50 + <Optimize>true</Optimize>
  51 + <OutputPath>bin\Release\</OutputPath>
  52 + <DefineConstants>TRACE</DefineConstants>
  53 + <ErrorReport>prompt</ErrorReport>
  54 + <WarningLevel>4</WarningLevel>
  55 + <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
  56 + </PropertyGroup>
  57 + <ItemGroup>
  58 + <Reference Include="System" />
  59 + <Reference Include="System.Core">
  60 + <RequiredTargetFramework>3.5</RequiredTargetFramework>
  61 + </Reference>
  62 + <Reference Include="System.Web" />
  63 + <Reference Include="System.Web.Abstractions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
  64 + <SpecificVersion>False</SpecificVersion>
  65 + <HintPath>..\Dependencies\System.Web.Abstractions.dll</HintPath>
  66 + </Reference>
  67 + <Reference Include="System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
  68 + <SpecificVersion>False</SpecificVersion>
  69 + <HintPath>..\Dependencies\System.Web.Routing.dll</HintPath>
  70 + </Reference>
  71 + <Reference Include="System.Xml.Linq">
  72 + <RequiredTargetFramework>3.5</RequiredTargetFramework>
  73 + </Reference>
  74 + <Reference Include="System.Data.DataSetExtensions">
  75 + <RequiredTargetFramework>3.5</RequiredTargetFramework>
  76 + </Reference>
  77 + <Reference Include="System.Data" />
  78 + <Reference Include="System.Xml" />
  79 + </ItemGroup>
  80 + <ItemGroup>
  81 + <Compile Include="DebugHttpHandler.cs" />
  82 + <Compile Include="DebugRoute.cs" />
  83 + <Compile Include="DebugRouteHandler.cs" />
  84 + <Compile Include="Properties\AssemblyInfo.cs" />
  85 + <Compile Include="RouteDebugger.cs" />
  86 + </ItemGroup>
  87 + <ItemGroup>
  88 + <BootstrapperPackage Include="Microsoft.Net.Client.3.5">
  89 + <Visible>False</Visible>
  90 + <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
  91 + <Install>false</Install>
  92 + </BootstrapperPackage>
  93 + <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
  94 + <Visible>False</Visible>
  95 + <ProductName>.NET Framework 3.5 SP1</ProductName>
  96 + <Install>true</Install>
  97 + </BootstrapperPackage>
  98 + <BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
  99 + <Visible>False</Visible>
  100 + <ProductName>Windows Installer 3.1</ProductName>
  101 + <Install>true</Install>
  102 + </BootstrapperPackage>
  103 + </ItemGroup>
  104 + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  105 + <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
  106 + Other similar extension points exist, see Microsoft.Common.targets.
  107 + <Target Name="BeforeBuild">
  108 + </Target>
  109 + <Target Name="AfterBuild">
  110 + </Target>
  111 + -->
  112 +</Project>
34 src/RouteDebug/RouteDebugger.cs
... ... @@ -0,0 +1,34 @@
  1 +using System.Web.Routing;
  2 +
  3 +namespace RouteDebug
  4 +{
  5 + public static class RouteDebugger
  6 + {
  7 + public static void RewriteRoutesForTesting(RouteCollection routes)
  8 + {
  9 + using (routes.GetReadLock())
  10 + {
  11 + bool foundDebugRoute = false;
  12 + foreach (RouteBase routeBase in routes)
  13 + {
  14 + Route route = routeBase as Route;
  15 + if (route != null)
  16 + {
  17 + route.RouteHandler = new DebugRouteHandler();
  18 + }
  19 +
  20 + if (route == DebugRoute.Singleton)
  21 + foundDebugRoute = true;
  22 +
  23 + }
  24 + if (!foundDebugRoute)
  25 + {
  26 + routes.Add(DebugRoute.Singleton);
  27 + }
  28 + }
  29 +
  30 +
  31 + }
  32 +
  33 + }
  34 +}
1  src/RouteDebuggerDemoWeb/Global.asax
... ... @@ -0,0 +1 @@
  1 +<%@ Application Codebehind="Global.asax.cs" Inherits="RouteTesterDemoWeb.GlobalApplication" Language="C#" %>
27 src/RouteDebuggerDemoWeb/Global.asax.cs
... ... @@ -0,0 +1,27 @@
  1 +using System;
  2 +using System.Web.Routing;
  3 +using System.Web.Mvc;
  4 +using System.Reflection;
  5 +
  6 +namespace RouteTesterDemoWeb
  7 +{
  8 + public class GlobalApplication : System.Web.HttpApplication
  9 + {
  10 + public static void RegisterRoutes(RouteCollection routes)
  11 + {
  12 + routes.MapRoute("foo-route", "foo/{id}", new { controller="Away", action = "Blah", id = (string)null });
  13 + routes.MapRoute("bar-route", "bar/{id}", new { controller = "Home", action = "Index", id = (string)null });
  14 + routes.MapRoute("token-route", "tokens/{id}", new {dataToken="BlahBlahBlah"});
  15 + routes.MapRoute("extension", "ext/{id}.mvc", new { controller = "Home", action = "Index", id = (string)null });
  16 + routes.MapRoute("mvc-default", "{controller}/{action}/{id}"
  17 + , new { controller = "Home", action = "Index", id = (string)null });
  18 + }
  19 +
  20 + protected void Application_Start(object sender, EventArgs e)
  21 + {
  22 + RouteTable.Routes.RouteExistingFiles = true;
  23 + RegisterRoutes(RouteTable.Routes);
  24 + RouteDebug.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes);
  25 + }
  26 + }
  27 +}
35 src/RouteDebuggerDemoWeb/Properties/AssemblyInfo.cs
... ... @@ -0,0 +1,35 @@
  1 +using System.Reflection;
  2 +using System.Runtime.CompilerServices;
  3 +using System.Runtime.InteropServices;
  4 +
  5 +// General Information about an assembly is controlled through the following
  6 +// set of attributes. Change these attribute values to modify the information
  7 +// associated with an assembly.
  8 +[assembly: AssemblyTitle("RouteTester")]
  9 +[assembly: AssemblyDescription("")]
  10 +[assembly: AssemblyConfiguration("")]
  11 +[assembly: AssemblyCompany("")]
  12 +[assembly: AssemblyProduct("RouteTester")]
  13 +[assembly: AssemblyCopyright("Copyright © 2008")]
  14 +[assembly: AssemblyTrademark("")]
  15 +[assembly: AssemblyCulture("")]
  16 +
  17 +// Setting ComVisible to false makes the types in this assembly not visible
  18 +// to COM components. If you need to access a type in this assembly from
  19 +// COM, set the ComVisible attribute to true on that type.
  20 +[assembly: ComVisible(false)]
  21 +
  22 +// The following GUID is for the ID of the typelib if this project is exposed to COM
  23 +[assembly: Guid("65160db0-ab4a-4fd7-b39b-98d5dbd258dd")]
  24 +
  25 +// Version information for an assembly consists of the following four values:
  26 +//
  27 +// Major Version
  28 +// Minor Version
  29 +// Build Number
  30 +// Revision
  31 +//
  32 +// You can specify all the values or you can default the Revision and Build Numbers
  33 +// by using the '*' as shown below:
  34 +[assembly: AssemblyVersion("1.0.0.0")]
  35 +[assembly: AssemblyFileVersion("1.0.0.0")]
114 src/RouteDebuggerDemoWeb/RouteDebuggerDemoWeb.csproj
... ... @@ -0,0 +1,114 @@
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  3 + <PropertyGroup>
  4 + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
  5 + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
  6 + <ProductVersion>9.0.30729</ProductVersion>
  7 + <SchemaVersion>2.0</SchemaVersion>
  8 + <ProjectGuid>{40F914CD-F707-4A4D-AF7B-75E8928055BB}</ProjectGuid>
  9 + <ProjectTypeGuids>{F85E285D-A4E0-4152-9332-AB1D724D3325};{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
  10 + <OutputType>Library</OutputType>
  11 + <AppDesignerFolder>Properties</AppDesignerFolder>
  12 + <RootNamespace>RouteDebug</RootNamespace>
  13 + <AssemblyName>RouteDebuggerDemoWeb</AssemblyName>
  14 + <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
  15 + <FileUpgradeFlags>
  16 + </FileUpgradeFlags>
  17 + <UpgradeBackupLocation>
  18 + </UpgradeBackupLocation>
  19 + <OldToolsVersion>3.5</OldToolsVersion>
  20 + <UseIISExpress>false</UseIISExpress>
  21 + <TargetFrameworkProfile />
  22 + </PropertyGroup>
  23 + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
  24 + <DebugSymbols>true</DebugSymbols>
  25 + <DebugType>full</DebugType>
  26 + <Optimize>false</Optimize>
  27 + <OutputPath>bin\</OutputPath>
  28 + <DefineConstants>DEBUG;TRACE</DefineConstants>
  29 + <ErrorReport>prompt</ErrorReport>
  30 + <WarningLevel>4</WarningLevel>
  31 + <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
  32 + <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
  33 + </PropertyGroup>
  34 + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
  35 + <DebugType>pdbonly</DebugType>
  36 + <Optimize>true</Optimize>
  37 + <OutputPath>bin\</OutputPath>
  38 + <DefineConstants>TRACE</DefineConstants>
  39 + <ErrorReport>prompt</ErrorReport>
  40 + <WarningLevel>4</WarningLevel>
  41 + <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
  42 + </PropertyGroup>
  43 + <ItemGroup>
  44 + <Reference Include="System" />
  45 + <Reference Include="System.ComponentModel.DataAnnotations" />
  46 + <Reference Include="System.Data" />
  47 + <Reference Include="System.Data.DataSetExtensions" />
  48 + <Reference Include="System.Web.Abstractions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
  49 + <SpecificVersion>False</SpecificVersion>
  50 + <HintPath>..\Dependencies\System.Web.Abstractions.dll</HintPath>
  51 + </Reference>
  52 + <Reference Include="System.Web.ApplicationServices" />
  53 + <Reference Include="System.Web.DynamicData" />
  54 + <Reference Include="System.Web.Entity" />
  55 + <Reference Include="System.Web.Extensions" />
  56 + <Reference Include="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
  57 + <Reference Include="System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
  58 + <SpecificVersion>False</SpecificVersion>
  59 + <HintPath>..\Dependencies\System.Web.Routing.dll</HintPath>
  60 + </Reference>
  61 + <Reference Include="System.Drawing" />
  62 + <Reference Include="System.Web" />
  63 + <Reference Include="System.Xml" />
  64 + <Reference Include="System.Configuration" />
  65 + <Reference Include="System.Web.Services" />
  66 + <Reference Include="System.EnterpriseServices" />
  67 + <Reference Include="System.Web.Mobile" />
  68 + <Reference Include="System.Xml.Linq" />
  69 + </ItemGroup>
  70 + <ItemGroup>
  71 + <Compile Include="Global.asax.cs">
  72 + <DependentUpon>Global.asax</DependentUpon>
  73 + </Compile>
  74 + <Compile Include="Properties\AssemblyInfo.cs" />
  75 + </ItemGroup>
  76 + <ItemGroup>
  77 + <Content Include="Global.asax" />
  78 + <Content Include="Web.config" />
  79 + </ItemGroup>
  80 + <ItemGroup>
  81 + <ProjectReference Include="..\RouteDebug\RouteDebug.csproj">
  82 + <Project>{09548C4E-F0C1-428A-BC3A-BFBB9328D7C7}</Project>
  83 + <Name>RouteDebug</Name>
  84 + </ProjectReference>
  85 + </ItemGroup>
  86 + <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
  87 + <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
  88 + <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
  89 + Other similar extension points exist, see Microsoft.Common.targets.
  90 + <Target Name="BeforeBuild">
  91 + </Target>
  92 + <Target Name="AfterBuild">
  93 + </Target>
  94 + -->
  95 + <ProjectExtensions>
  96 + <VisualStudio>
  97 + <FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
  98 + <WebProjectProperties>
  99 + <UseIIS>False</UseIIS>
  100 + <AutoAssignPort>True</AutoAssignPort>
  101 + <DevelopmentServerPort>14230</DevelopmentServerPort>
  102 + <DevelopmentServerVPath>/</DevelopmentServerVPath>
  103 + <IISUrl>
  104 + </IISUrl>
  105 + <NTLMAuthentication>False</NTLMAuthentication>
  106 + <UseCustomServer>False</UseCustomServer>
  107 + <CustomServerUrl>
  108 + </CustomServerUrl>
  109 + <SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
  110 + </WebProjectProperties>
  111 + </FlavorProperties>
  112 + </VisualStudio>
  113 + </ProjectExtensions>
  114 +</Project>
53 src/RouteDebuggerDemoWeb/Web.config
... ... @@ -0,0 +1,53 @@
  1 +<?xml version="1.0"?>
  2 +<!--
  3 + For more information on how to configure your ASP.NET application, please visit
  4 + http://go.microsoft.com/fwlink/?LinkId=152368
  5 + -->
  6 +
  7 +<configuration>
  8 + <appSettings>
  9 + <add key="ClientValidationEnabled" value="true"/>
  10 + <add key="UnobtrusiveJavaScriptEnabled" value="true"/>
  11 + </appSettings>
  12 +
  13 + <system.web>
  14 + <compilation debug="true" targetFramework="4.0">
  15 + <assemblies>
  16 + <add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
  17 + <add assembly="System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
  18 + <add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
  19 + <add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
  20 + <add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
  21 + </assemblies>
  22 + </compilation>
  23 +
  24 + <authentication mode="Forms">
  25 + <forms loginUrl="~/Account/LogOn" timeout="2880" />
  26 + </authentication>
  27 +
  28 + <pages>
  29 + <namespaces>
  30 + <add namespace="System.Web.Helpers" />
  31 + <add namespace="System.Web.Mvc" />
  32 + <add namespace="System.Web.Mvc.Ajax" />
  33 + <add namespace="System.Web.Mvc.Html" />
  34 + <add namespace="System.Web.Routing" />
  35 + <add namespace="System.Web.WebPages"/>
  36 + </namespaces>
  37 + </pages>
  38 + </system.web>
  39 +
  40 + <system.webServer>
  41 + <validation validateIntegratedModeConfiguration="false"/>
  42 + <modules runAllManagedModulesForAllRequests="true"/>
  43 + </system.webServer>
  44 +
  45 + <runtime>
  46 + <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
  47 + <dependentAssembly>
  48 + <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
  49 + <bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="3.0.0.0" />
  50 + </dependentAssembly>
  51 + </assemblyBinding>
  52 + </runtime>
  53 +</configuration>
11 src/RouteMagic.Mvc/RouteMagic.Mvc.csproj
@@ -30,10 +30,18 @@
30 30 <ErrorReport>prompt</ErrorReport>
31 31 <WarningLevel>4</WarningLevel>
32 32 </PropertyGroup>
  33 + <PropertyGroup>
  34 + <SignAssembly>true</SignAssembly>
  35 + </PropertyGroup>
  36 + <PropertyGroup>
  37 + <AssemblyOriginatorKeyFile>RouteMagickKey.snk</AssemblyOriginatorKeyFile>
  38 + </PropertyGroup>
33 39 <ItemGroup>
34 40 <Reference Include="System" />
  41 + <Reference Include="System.ComponentModel.DataAnnotations" />
35 42 <Reference Include="System.Core" />
36 43 <Reference Include="System.Web" />
  44 + <Reference Include="System.Web.ApplicationServices" />
37 45 <Reference Include="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
38 46 <Reference Include="System.Xml.Linq" />
39 47 <Reference Include="System.Data.DataSetExtensions" />
@@ -55,6 +63,9 @@
55 63 <Name>RouteMagic</Name>
56 64 </ProjectReference>
57 65 </ItemGroup>
  66 + <ItemGroup>
  67 + <None Include="RouteMagickKey.snk" />
  68 + </ItemGroup>
58 69 <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
59 70 <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
60 71 Other similar extension points exist, see Microsoft.Common.targets.
BIN  src/RouteMagic.Mvc/RouteMagickKey.snk
Binary file not shown
12 src/RouteMagic.sln
@@ -14,6 +14,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Metadata", "Metadata", "{88
14 14 SolutionInfo.cs = SolutionInfo.cs
15 15 EndProjectSection
16 16 EndProject
  17 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RouteDebug", "RouteDebug\RouteDebug.csproj", "{09548C4E-F0C1-428A-BC3A-BFBB9328D7C7}"
  18 +EndProject
  19 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RouteDebuggerDemoWeb", "RouteDebuggerDemoWeb\RouteDebuggerDemoWeb.csproj", "{40F914CD-F707-4A4D-AF7B-75E8928055BB}"
  20 +EndProject
17 21 Global
18 22 GlobalSection(SolutionConfigurationPlatforms) = preSolution
19 23 Debug|Any CPU = Debug|Any CPU
@@ -36,6 +40,14 @@ Global
36 40 {D9321DA3-F3AC-4567-8452-B1B5D5633045}.Debug|Any CPU.Build.0 = Debug|Any CPU
37 41 {D9321DA3-F3AC-4567-8452-B1B5D5633045}.Release|Any CPU.ActiveCfg = Release|Any CPU
38 42 {D9321DA3-F3AC-4567-8452-B1B5D5633045}.Release|Any CPU.Build.0 = Release|Any CPU
  43 + {09548C4E-F0C1-428A-BC3A-BFBB9328D7C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  44 + {09548C4E-F0C1-428A-BC3A-BFBB9328D7C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
  45 + {09548C4E-F0C1-428A-BC3A-BFBB9328D7C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
  46 + {09548C4E-F0C1-428A-BC3A-BFBB9328D7C7}.Release|Any CPU.Build.0 = Release|Any CPU
  47 + {40F914CD-F707-4A4D-AF7B-75E8928055BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  48 + {40F914CD-F707-4A4D-AF7B-75E8928055BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
  49 + {40F914CD-F707-4A4D-AF7B-75E8928055BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
  50 + {40F914CD-F707-4A4D-AF7B-75E8928055BB}.Release|Any CPU.Build.0 = Release|Any CPU
39 51 EndGlobalSection
40 52 GlobalSection(SolutionProperties) = preSolution
41 53 HideSolutionNode = FALSE
9 src/RouteMagic/RouteMagic.csproj
@@ -30,6 +30,12 @@
30 30 <ErrorReport>prompt</ErrorReport>
31 31 <WarningLevel>4</WarningLevel>
32 32 </PropertyGroup>
  33 + <PropertyGroup>
  34 + <SignAssembly>true</SignAssembly>
  35 + </PropertyGroup>
  36 + <PropertyGroup>
  37 + <AssemblyOriginatorKeyFile>RouteMagickKey.snk</AssemblyOriginatorKeyFile>
  38 + </PropertyGroup>
33 39 <ItemGroup>
34 40 <Reference Include="System" />
35 41 <Reference Include="System.Core" />
@@ -64,6 +70,9 @@
64 70 <Compile Include="RouteRegistrationExtensions.cs" />
65 71 <Compile Include="RouteValueDictionaryExtensions.cs" />
66 72 </ItemGroup>
  73 + <ItemGroup>
  74 + <None Include="RouteMagickKey.snk" />
  75 + </ItemGroup>
67 76 <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
68 77 <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
69 78 Other similar extension points exist, see Microsoft.Common.targets.
BIN  src/RouteMagic/RouteMagickKey.snk
Binary file not shown
3  src/RouteMagicDemo.Web/RouteMagicDemo.Web.csproj
@@ -14,6 +14,7 @@
14 14 <AssemblyName>RouteMagicDemo.Web</AssemblyName>
15 15 <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
16 16 <MvcBuildViews>false</MvcBuildViews>
  17 + <UseIISExpress>false</UseIISExpress>
17 18 </PropertyGroup>
18 19 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
19 20 <DebugSymbols>true</DebugSymbols>
@@ -33,6 +34,7 @@
33 34 <WarningLevel>4</WarningLevel>
34 35 </PropertyGroup>
35 36 <ItemGroup>
  37 + <Reference Include="System.Web.Extensions" />
36 38 <Reference Include="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
37 39 <Reference Include="System.Web.WebPages" />
38 40 <Reference Include="System.Web.Helpers" />
@@ -48,7 +50,6 @@
48 50 <Reference Include="System.Data.DataSetExtensions" />
49 51 <Reference Include="System.Xml.Linq" />
50 52 <Reference Include="System.Web" />
51   - <Reference Include="System.Web.Extensions" />
52 53 <Reference Include="System.Web.Abstractions" />
53 54 <Reference Include="System.Web.Routing" />
54 55 <Reference Include="System.Xml" />
BIN  src/RouteMagickKey.snk
Binary file not shown
47 src/UnitTests/RouteEvaluator.cs
... ... @@ -0,0 +1,47 @@
  1 +using System.Collections.Generic;
  2 +using System.Web;
  3 +using System.Web.Routing;
  4 +using Moq;
  5 +
  6 +namespace UnitTests
  7 +{
  8 + public class RouteEvaluator
  9 + {
  10 + RouteCollection routes;
  11 +
  12 + public RouteEvaluator(RouteCollection routes)
  13 + {
  14 + this.routes = routes;
  15 + }
  16 +
  17 + public IList<RouteData> GetMatches(string virtualPath)
  18 + {
  19 + return GetMatches(virtualPath, "GET");
  20 + }
  21 +
  22 + public IList<RouteData> GetMatches(string virtualPath, string httpMethod)
  23 + {
  24 + List<RouteData> matchingRouteData = new List<RouteData>();
  25 +
  26 + foreach (var route in this.routes)
  27 + {
  28 + var context = new Mock<HttpContextBase>();
  29 + var request = new Mock<HttpRequestBase>();
  30 +
  31 + context.Setup(ctx => ctx.Request).Returns(request.Object);
  32 + request.Setup(req => req.PathInfo).Returns(string.Empty);
  33 + request.Setup(req => req.AppRelativeCurrentExecutionFilePath).Returns(virtualPath);
  34 + if (!string.IsNullOrEmpty(httpMethod))
  35 + {
  36 + request.Setup(req => req.HttpMethod).Returns(httpMethod);
  37 + }
  38 +
  39 + RouteData routeData = this.routes.GetRouteData(context.Object);
  40 + if (routeData != null)
  41 + matchingRouteData.Add(routeData);
  42 + }
  43 +
  44 + return matchingRouteData;
  45 + }
  46 + }
  47 +}
89 src/UnitTests/RouteMockHelpers.cs
... ... @@ -0,0 +1,89 @@
  1 +using System;
  2 +using System.Collections.Specialized;
  3 +using System.Web;
  4 +using Moq;
  5 +
  6 +namespace UnitTests
  7 +{
  8 + public static class RoutingMockHelpers
  9 + {
  10 + public static HttpContextBase FakeHttpContext()
  11 + {
  12 + var context = new Mock<HttpContextBase>();
  13 + var request = new Mock<HttpRequestBase>();
  14 + var response = new Mock<HttpResponseBase>();
  15 + var session = new Mock<HttpSessionStateBase>();
  16 + var server = new Mock<HttpServerUtilityBase>();
  17 +
  18 + context.Setup(ctx => ctx.Request).Returns(request.Object);
  19 + context.Setup(ctx => ctx.Response).Returns(response.Object);
  20 + context.Setup(ctx => ctx.Session).Returns(session.Object);
  21 + context.Setup(ctx => ctx.Server).Returns(server.Object);
  22 +
  23 + return context.Object;
  24 + }
  25 +
  26 + public static HttpContextBase FakeHttpContext(string virtualPath)
  27 + {
  28 + HttpContextBase context = FakeHttpContext();
  29 + context.Request.SetupRequestUrl(virtualPath);
  30 + return context;
  31 + }
  32 +
  33 + static string GetUrlFileName(string url)
  34 + {
  35 + if (url.Contains("?"))
  36 + return url.Substring(0, url.IndexOf("?"));
  37 + else
  38 + return url;
  39 + }
  40 +
  41 + static NameValueCollection GetQueryStringParameters(string url)
  42 + {
  43 + if (url.Contains("?"))
  44 + {
  45 + NameValueCollection parameters = new NameValueCollection();
  46 +
  47 + string[] parts = url.Split("?".ToCharArray());
  48 + string[] keys = parts[1].Split("&".ToCharArray());
  49 +
  50 + foreach (string key in keys)
  51 + {
  52 + string[] part = key.Split("=".ToCharArray());
  53 + parameters.Add(part[0], part[1]);
  54 + }
  55 +
  56 + return parameters;
  57 + }
  58 + else
  59 + {
  60 + return null;
  61 + }
  62 + }
  63 +
  64 + public static void SetHttpMethodResult(this HttpRequestBase request, string httpMethod)
  65 + {
  66 + Mock.Get(request)
  67 + .Setup(req => req.HttpMethod)
  68 + .Returns(httpMethod);
  69 + }
  70 +
  71 + public static void SetupRequestUrl(this HttpRequestBase request, string url)
  72 + {
  73 + if (url == null)
  74 + throw new ArgumentNullException("url");
  75 +
  76 + if (!url.StartsWith("~/"))
  77 + throw new ArgumentException("Sorry, we expect a virtual url starting with \"~/\".");
  78 +
  79 + var mock = Mock.Get(request);
  80 +
  81 + mock.Setup(req => req.QueryString)
  82 + .Returns(GetQueryStringParameters(url));
  83 + mock.Setup(req => req.AppRelativeCurrentExecutionFilePath)
  84 + .Returns(GetUrlFileName(url));
  85 + mock.Setup(req => req.PathInfo)
  86 + .Returns(string.Empty);
  87 + }
  88 + }
  89 +}
74 src/UnitTests/RouteTests.cs
... ... @@ -0,0 +1,74 @@
  1 +using System.Web;
  2 +using System.Web.Routing;
  3 +using Moq;
  4 +using RouteTesterDemoWeb;
  5 +using Xunit;
  6 +
  7 +namespace UnitTests
  8 +{
  9 + public class RouteTests
  10 + {
  11 + [Fact]
  12 + public void CanMatchUsingRouteEvaluator()
  13 + {
  14 + var routes = new RouteCollection();
  15 + GlobalApplication.RegisterRoutes(routes);
  16 +
  17 + var evaluator = new RouteEvaluator(routes);
  18 + var matchingRouteData = evaluator.GetMatches("~/foo/bar");
  19 + Assert.True(matchingRouteData.Count > 0);
  20 + matchingRouteData = evaluator.GetMatches("~/foo/bar/baz/quux/yadda/billy");
  21 + Assert.Equal(0, matchingRouteData.Count);
  22 + }
  23 +
  24 + [Fact]
  25 + public void CanMatchFooRouteTheShortWay()
  26 + {
  27 + var routes = new RouteCollection();
  28 + GlobalApplication.RegisterRoutes(routes);
  29 +
  30 + var context = RoutingMockHelpers.FakeHttpContext("~/foo/bar");
  31 + var routeData = routes.GetRouteData(context);
  32 + Assert.Equal("bar", routeData.Values["id"]);
  33 +
  34 + //Assert defaults.
  35 + Assert.Equal("Away", routeData.Values["controller"]);
  36 + Assert.Equal("Blah", routeData.Values["action"]);
  37 + }
  38 +
  39 + [Fact]
  40 + public void CanMatchFooTheLongWay()
  41 + {
  42 + var routes = new RouteCollection();
  43 + GlobalApplication.RegisterRoutes(routes);
  44 +
  45 + var contextMock = new Mock<HttpContextBase>();
  46 + var requestMock = new Mock<HttpRequestBase>();
  47 +
  48 + contextMock.Setup(ctx => ctx.Request).Returns(requestMock.Object);
  49 + requestMock.Setup(req => req.PathInfo).Returns(string.Empty);
  50 + requestMock.Setup(req => req.AppRelativeCurrentExecutionFilePath).Returns("~/foo/bar");
  51 +
  52 + var routeData = routes.GetRouteData(contextMock.Object);
  53 + Assert.Equal("bar", routeData.Values["id"]);
  54 +
  55 + //Assert defaults.
  56 + Assert.Equal("Away", routeData.Values["controller"]);
  57 + Assert.Equal("Blah", routeData.Values["action"]);
  58 + }
  59 +
  60 + [Fact]
  61 + public void CanMatchEmptyUrl()
  62 + {
  63 + RouteCollection routes = new RouteCollection();
  64 + routes.Add(new Route(string.Empty, new Mock<IRouteHandler>().Object)
  65 + { Defaults = new RouteValueDictionary(new { controller = "Home" }) });
  66 +
  67 + var context = RoutingMockHelpers.FakeHttpContext("~/");
  68 + var routeData = routes.GetRouteData(context);
  69 + Assert.NotNull(routeData);
  70 + Assert.Equal("Home", routeData.Values["controller"]);
  71 +
  72 + }
  73 + }
  74 +}
9 src/UnitTests/UnitTests.csproj
@@ -38,8 +38,10 @@
38 38 <HintPath>..\packages\PowerAssert.1.0\PowerAssert.dll</HintPath>
39 39 </Reference>
40 40 <Reference Include="System" />
  41 + <Reference Include="System.ComponentModel.DataAnnotations" />
41 42 <Reference Include="System.Core" />
42 43 <Reference Include="System.Web" />
  44 + <Reference Include="System.Web.ApplicationServices" />
43 45 <Reference Include="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
44 46 <Reference Include="System.Xml.Linq" />
45 47 <Reference Include="System.Data.DataSetExtensions" />
@@ -54,6 +56,9 @@
54 56 </Reference>
55 57 </ItemGroup>
56 58 <ItemGroup>
  59 + <Compile Include="RouteEvaluator.cs" />
  60 + <Compile Include="RouteMockHelpers.cs" />
  61 + <Compile Include="RouteTests.cs" />
57 62 <Compile Include="RouteValueDictionaryExtensionsTest.cs" />
58 63 <Compile Include="GroupRouteTest.cs" />
59 64 <Compile Include="HttpHandlers\DelegateHttpHandlerTest.cs" />
@@ -70,6 +75,10 @@
70 75 <WCFMetadata Include="Service References\" />
71 76 </ItemGroup>
72 77 <ItemGroup>
  78 + <ProjectReference Include="..\RouteDebuggerDemoWeb\RouteDebuggerDemoWeb.csproj">
  79 + <Project>{40F914CD-F707-4A4D-AF7B-75E8928055BB}</Project>
  80 + <Name>RouteDebuggerDemoWeb</Name>
  81 + </ProjectReference>
73 82 <ProjectReference Include="..\RouteMagic.Mvc\RouteMagic.Mvc.csproj">
74 83 <Project>{D9321DA3-F3AC-4567-8452-B1B5D5633045}</Project>
75 84 <Name>RouteMagic.Mvc</Name>

0 comments on commit 8819e49

Please sign in to comment.
Something went wrong with that request. Please try again.