diff --git a/core/includes/common.inc b/core/includes/common.inc
index c47494e..aa5a32c 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -4,6 +4,7 @@ use Drupal\Component\Utility\NestedArray;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 use Drupal\Core\Cache\CacheBackendInterface;
+use Symfony\Component\DependencyInjection\Container;
 use Drupal\Core\Database\Database;
 use Drupal\Core\Template\Attribute;
 
@@ -6832,6 +6833,7 @@ function drupal_flush_all_caches() {
   // Rebuild the menu router based on all rebuilt data.
   // Important: This rebuild must happen last, so the menu router is guaranteed
   // to be based on up to date information.
+  drupal_container()->get('router.builder')->rebuild();
   menu_router_rebuild();
 
   // Re-initialize the maintenance theme, if the current request attempted to
diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php
index 4ad6726..f671c57 100644
--- a/core/lib/Drupal/Core/CoreBundle.php
+++ b/core/lib/Drupal/Core/CoreBundle.php
@@ -15,6 +15,8 @@ use Symfony\Component\DependencyInjection\Scope;
 use Symfony\Component\HttpKernel\Bundle\Bundle;
 use Symfony\Component\DependencyInjection\Compiler\PassConfig;
 
+use Drupal\Core\Database\Database;
+
 /**
  * Bundle class for mandatory core services.
  *
@@ -53,12 +55,25 @@ class CoreBundle extends Bundle
       ->setFactoryMethod('getConnection')
       ->addArgument('slave');
 
+    $container->register('router.dumper', '\Drupal\Core\Routing\MatcherDumper')
+      ->addArgument(new Reference('database'));
+    $container->register('router.builder', 'Drupal\Core\Routing\RouteBuilder')
+      ->addArgument(new Reference('router.dumper'));
+
     // @todo Replace below lines with the commented out block below it when it's
     //   performant to do so: http://drupal.org/node/1706064.
     $dispatcher = $container->get('dispatcher');
-    $matcher = new \Drupal\Core\LegacyUrlMatcher();
+    $matcher = new \Drupal\Core\Routing\ChainMatcher();
+    $matcher->add(new \Drupal\Core\LegacyUrlMatcher());
+
+    $nested = new \Drupal\Core\Routing\NestedMatcher();
+    $nested->setInitialMatcher(new \Drupal\Core\Routing\PathMatcher(Database::getConnection()));
+    $nested->addPartialMatcher(new \Drupal\Core\Routing\HttpMethodMatcher());
+    $nested->setFinalMatcher(new \Drupal\Core\Routing\FirstEntryFinalMatcher());
+    $matcher->add($nested, 5);
+
     $content_negotation = new \Drupal\Core\ContentNegotiation();
-    $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\RouterListener($matcher));
+    $dispatcher->addSubscriber(new \Symfony\Component\HttpKernel\EventListener\RouterListener($matcher));
     $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\ViewSubscriber($content_negotation));
     $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\AccessSubscriber());
     $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\MaintenanceModeSubscriber());
@@ -68,6 +83,7 @@ class CoreBundle extends Bundle
     $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\FinishResponseSubscriber());
     $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\RequestCloseSubscriber());
     $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\ConfigGlobalOverrideSubscriber());
+    $dispatcher->addSubscriber(new \Drupal\Core\EventSubscriber\RouteProcessorSubscriber());
     $container->set('content_negotiation', $content_negotation);
     $dispatcher->addSubscriber(\Drupal\Core\ExceptionController::getExceptionListener($container));
 
diff --git a/core/lib/Drupal/Core/EventSubscriber/RouteProcessorSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/RouteProcessorSubscriber.php
new file mode 100644
index 0000000..cba2fce
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/RouteProcessorSubscriber.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\EventSubscriber\RouteProcessorSubscriber.
+ */
+
+namespace Drupal\Core\EventSubscriber;
+
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
+use Symfony\Component\HttpKernel\Log\LoggerInterface;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+
+/**
+ * Listener to process request controller information.
+ */
+class RouteProcessorSubscriber implements EventSubscriberInterface {
+
+  /**
+   * Sets a default controller for a route if one was not specified.
+   */
+  public function onRequestSetController(GetResponseEvent $event) {
+    $request = $event->getRequest();
+
+    if (!$request->attributes->has('_controller') && $request->attributes->has('_content')) {
+      $request->attributes->set('_controller', '\Drupal\Core\HtmlPageController::content');
+    }
+  }
+
+  /**
+   * Registers the methods in this class that should be listeners.
+   *
+   * @return array
+   *   An array of event listener definitions.
+   */
+  static function getSubscribedEvents() {
+    // The RouterListener has priority 32, and we need to run after that.
+    $events[KernelEvents::REQUEST][] = array('onRequestSetController', 30);
+
+    return $events;
+  }
+}
diff --git a/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php
index 637d48d..cb3aeea 100644
--- a/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php
@@ -10,6 +10,7 @@ namespace Drupal\Core\EventSubscriber;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpFoundation\JsonResponse;
 use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
 use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
@@ -44,15 +45,31 @@ class ViewSubscriber implements EventSubscriberInterface {
    */
   public function onView(GetResponseForControllerResultEvent $event) {
 
-    $request = $event->getRequest();
-
-    $method = 'on' . $this->negotiation->getContentType($request);
-
-    if (method_exists($this, $method)) {
-      $event->setResponse($this->$method($event));
+    // For a master request, we process the result and wrap it as needed.
+    // For a subrequest, all we want is the string value.  We assume that
+    // is just an HTML string from a controller, so wrap that into a response
+    // object.  The subrequest's response will get dissected and placed into
+    // the larger page as needed.
+    if ($event->getRequestType() == HttpKernelInterface::MASTER_REQUEST) {
+      $request = $event->getRequest();
+
+      $method = 'on' . $this->negotiation->getContentType($request);
+
+      if (method_exists($this, $method)) {
+        $event->setResponse($this->$method($event));
+      }
+      else {
+        $event->setResponse(new Response('Unsupported Media Type', 415));
+      }
     }
     else {
-      $event->setResponse(new Response('Unsupported Media Type', 415));
+      $page_result = $event->getControllerResult();
+      if (!is_array($page_result)) {
+        $page_result = array(
+          '#markup' => $page_result,
+        );
+      }
+      $event->setResponse(new Response(drupal_render_page($page_result)));
     }
   }
 
diff --git a/core/lib/Drupal/Core/HtmlPageController.php b/core/lib/Drupal/Core/HtmlPageController.php
new file mode 100644
index 0000000..863f33f
--- /dev/null
+++ b/core/lib/Drupal/Core/HtmlPageController.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\HtmlPageController.
+ */
+
+namespace Drupal\Core;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\DependencyInjection\ContainerAwareInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Default controller for most HTML pages.
+ */
+class HtmlPageController implements ContainerAwareInterface {
+
+  /**
+   * The injection container for this object.
+   *
+   * @var ContainerInterface
+   */
+  protected $container;
+
+  /**
+   * Injects the service container used by this object.
+   *
+   * @param ContainerInterface $container
+   *   The service container this object should use.
+   */
+  public function setContainer(ContainerInterface $container = NULL) {
+    $this->container = $container;
+  }
+
+  /**
+   * Controller method for generic HTML pages.
+   *
+   * @param Request $request
+   *   The request object.
+   * @param type $_content
+   *   The body content callable that contains the body region of this page.
+   * @return \Symfony\Component\HttpFoundation\Response
+   */
+  public function content(Request $request, $_content) {
+
+    // @todo When we have a Generator, we can replace the forward() call with
+    // a render() call, which would handle ESI and hInclude as well.  That will
+    // require an _internal route.  For examples, see:
+    // https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing/internal.xml
+    // https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Controller/InternalController.php
+    $attributes = $request->attributes;
+    $controller = $_content;
+    $attributes->remove('system_path');
+    $attributes->remove('_content');
+    $response = $this->container->get('http_kernel')->forward($controller, $attributes->all(), $request->query->all());
+
+    $page_content = $response->getContent();
+
+    return new Response(drupal_render_page($page_content));
+  }
+}
diff --git a/core/lib/Drupal/Core/LegacyUrlMatcher.php b/core/lib/Drupal/Core/LegacyUrlMatcher.php
index 8828f36..48987a9 100644
--- a/core/lib/Drupal/Core/LegacyUrlMatcher.php
+++ b/core/lib/Drupal/Core/LegacyUrlMatcher.php
@@ -9,13 +9,14 @@ namespace Drupal\Core;
 
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\Routing\Exception\ResourceNotFoundException;
-use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
+use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
+use Symfony\Component\Routing\RequestContextAwareInterface;
 use Symfony\Component\Routing\RequestContext;
 
 /**
  * UrlMatcher matches URL based on a set of routes.
  */
-class LegacyUrlMatcher implements UrlMatcherInterface {
+class LegacyUrlMatcher implements RequestMatcherInterface, RequestContextAwareInterface {
 
   /**
    * The request context for this matcher.
@@ -98,8 +99,8 @@ class LegacyUrlMatcher implements UrlMatcherInterface {
    *
    * @api
    */
-  public function match($pathinfo) {
-    if ($router_item = $this->matchDrupalItem($pathinfo)) {
+  public function matchRequest(Request $request) {
+    if ($router_item = $this->matchDrupalItem($request->attributes->get('system_path'))) {
       $ret = $this->convertDrupalItem($router_item);
       // Stash the router item in the attributes while we're transitioning.
       $ret['drupal_menu_item'] = $router_item;
diff --git a/core/lib/Drupal/Core/Routing/ChainMatcher.php b/core/lib/Drupal/Core/Routing/ChainMatcher.php
new file mode 100644
index 0000000..cf8a66d
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/ChainMatcher.php
@@ -0,0 +1,159 @@
+<?php
+
+namespace Drupal\Core\Routing;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\Exception\RouteNotFoundException;
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+use Symfony\Component\Routing\RequestContextAwareInterface;
+use Symfony\Component\Routing\RequestContext;
+
+/**
+ * Aggregates multiple matchers together in series.
+ *
+ * The RequestContext is entirely unused. It's included only to satisfy the
+ * interface needed for RouterListener.  Hopefully we can remove it later.
+ */
+class ChainMatcher implements RequestMatcherInterface, RequestContextAwareInterface {
+
+  /**
+   * Array of RequestMatcherInterface objects to be checked in order.
+   *
+   * @var array
+   */
+  protected $matchers = array();
+
+  /**
+   * Array of RequestMatcherInterface objects, sorted.
+   * @var type
+   */
+  protected $sortedMatchers = array();
+
+  /**
+   * The request context for this matcher.
+   *
+   * This is unused.  It's just to satisfy the interface.
+   *
+   * @var Symfony\Component\Routing\RequestContext
+   */
+  protected $context;
+
+  /**
+   * Constructor.
+   */
+  public function __construct() {
+    // We will not actually use this object, but it's needed to conform to
+    // the interface.
+    $this->context = new RequestContext();
+  }
+
+  /**
+   * Sets the request context.
+   *
+   * This method is just to satisfy the interface, and is largely vestigial.
+   * The request context object does not contain the information we need, so
+   * we will use the original request object.
+   *
+   * @param Symfony\Component\Routing\RequestContext $context
+   *   The context.
+   *
+   * @api
+   */
+  public function setContext(RequestContext $context) {
+    $this->context = $context;
+  }
+
+  /**
+   * Gets the request context.
+   *
+   * This method is just to satisfy the interface, and is largely vestigial.
+   * The request context object does not contain the information we need, so
+   * we will use the original request object.
+   *
+   * @return Symfony\Component\Routing\RequestContext
+   *   The context.
+   */
+  public function getContext() {
+    return $this->context;
+  }
+
+  /**
+   * Matches a request against all queued matchers.
+   *
+   * @param Request $request The request to match
+   *
+   * @return array An array of parameters
+   *
+   * @throws ResourceNotFoundException If no matching resource could be found
+   * @throws MethodNotAllowedException If a matching resource was found but the request method is not allowed
+   */
+  public function matchRequest(Request $request) {
+    $methodNotAllowed = null;
+
+    foreach ($this->all() as $matcher) {
+      try {
+        return $matcher->matchRequest($request);
+      } catch (ResourceNotFoundException $e) {
+        // Needs special care
+      } catch (MethodNotAllowedException $e) {
+        $methodNotAllowed = $e;
+      }
+    }
+
+    throw $methodNotAllowed ?: new ResourceNotFoundException("None of the matchers in the chain matched this request.");
+  }
+
+  /**
+    * Adds a Matcher to the index.
+    *
+    * @param MatcherInterface $matcher
+    *   The matcher to add.
+    * @param int $priority
+    *   The priority of the matcher. Higher number matchers will be checked
+    *   first.
+    */
+  public function add(RequestMatcherInterface $matcher, $priority = 0) {
+    if (empty($this->matchers[$priority])) {
+      $this->matchers[$priority] = array();
+    }
+
+    $this->matchers[$priority][] = $matcher;
+    $this->sortedMatchers = array();
+  }
+
+  /**
+    * Sorts the matchers and flattens them.
+    *
+    * @return array
+    *   An array of RequestMatcherInterface objects.
+    */
+  public function all() {
+    if (empty($this->sortedMatchers)) {
+      $this->sortedMatchers = $this->sortMatchers();
+    }
+
+    return $this->sortedMatchers;
+  }
+
+  /**
+    * Sort matchers by priority.
+    *
+    * The highest priority number is the highest priority (reverse sorting).
+    *
+    * @return \Symfony\Component\Routing\RequestMatcherInterface[]
+    *   An array of Matcher objects in the order they should be used.
+    */
+  protected function sortMatchers() {
+    $sortedMatchers = array();
+    krsort($this->matchers);
+
+    foreach ($this->matchers as $matchers) {
+      $sortedMatchers = array_merge($sortedMatchers, $matchers);
+    }
+
+    return $sortedMatchers;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Routing/CompiledRoute.php b/core/lib/Drupal/Core/Routing/CompiledRoute.php
new file mode 100644
index 0000000..1600e76
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/CompiledRoute.php
@@ -0,0 +1,163 @@
+<?php
+
+namespace Drupal\Core\Routing;
+
+use Symfony\Component\Routing\Route;
+
+/**
+ * Description of CompiledRoute
+ */
+class CompiledRoute {
+
+  /**
+   * The fitness of this route.
+   *
+   * @var int
+   */
+  protected $fit;
+
+  /**
+   * The pattern outline of this route.
+   *
+   * @var string
+   */
+  protected $patternOutline;
+
+  /**
+   * The number of parts in the path of this route.
+   *
+   * @var int
+   */
+  protected $numParts;
+
+  /**
+   * The Route object of which this object is the compiled version.
+   *
+   * @var Symfony\Component\Routing\Route
+   */
+  protected $route;
+
+  protected $variables;
+  protected $tokens;
+  protected $staticPrefix;
+  protected $regex;
+
+
+  /**
+   * Constructs a new CompiledRoute object.
+   *
+   * @param Route  $route
+   *   A original Route instance.
+   * @param int $fit
+   *   The fitness of the route.
+   * @param string $fit
+   *   The pattern outline for this route.
+   *  @param int $num_parts
+   *   The number of parts in the path.
+   *  @param string $regex
+   *   The regular expression to match placeholders out of this path.
+   */
+  public function __construct(Route $route, $fit, $pattern_outline, $num_parts, $regex) {
+    $this->route = $route;
+    $this->fit = $fit;
+    $this->patternOutline = $pattern_outline;
+    $this->numParts = $num_parts;
+    $this->regex = $regex;
+  }
+
+  /**
+   * Returns the fit of this route
+   *
+   * See RouteCompiler for a definition of how the fit is calculated.
+   *
+   * @return int
+   *   The fit of the route.
+   */
+  public function getFit() {
+    return $this->fit;
+  }
+
+  /**
+   * Returns the number of parts in this route's path.
+   *
+   * The string "foo/bar/baz" has 3 parts, regardless of how many of them are
+   * placeholders.
+   *
+   * @return int
+   *   The number of parts in the path.
+   */
+  public function getNumParts() {
+    return $this->numParts;
+  }
+
+  /**
+   * Returns the pattern outline of this route.
+   *
+   * The pattern outline of a route is the path pattern of the route, but
+   * normalized such that all placeholders are replaced with %.
+   *
+   * @return string
+   *   The normalized path pattern.
+   */
+  public function getPatternOutline() {
+    return $this->patternOutline;
+  }
+
+  /**
+   * Returns the placeholder regex.
+   *
+   * @return string
+   *   The regex to locate placeholders in this pattern.
+   */
+  public function getRegex() {
+    return $this->regex;
+  }
+
+  /**
+    * Returns the Route instance.
+    *
+    * @return Route
+    *   A Route instance
+    */
+  public function getRoute() {
+    return $this->route;
+  }
+
+  /**
+    * Returns the pattern.
+    *
+    * @return string The pattern
+    */
+  public function getPattern() {
+    return $this->route->getPattern();
+  }
+
+  /**
+    * Returns the options.
+    *
+    * @return array The options
+    */
+  public function getOptions() {
+    return $this->route->getOptions();
+  }
+
+  /**
+    * Returns the defaults.
+    *
+    * @return array The defaults
+    */
+  public function getDefaults() {
+    return $this->route->getDefaults();
+  }
+
+  /**
+    * Returns the requirements.
+    *
+    * @return array The requirements
+    */
+  public function getRequirements() {
+    return $this->route->getRequirements();
+  }
+
+}
+
diff --git a/core/lib/Drupal/Core/Routing/FinalMatcherInterface.php b/core/lib/Drupal/Core/Routing/FinalMatcherInterface.php
new file mode 100644
index 0000000..ae2bba0
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/FinalMatcherInterface.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Drupal\Core\Routing;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * A FinalMatcher returns only one route from a collection of candidate routes.
+ */
+interface FinalMatcherInterface {
+
+  /**
+   * Sets the route collection this matcher should use.
+   *
+   * @param RouteCollection $collection
+   *   The collection against which to match.
+   *
+   * @return FinalMatcherInterface
+   *   The current matcher.
+   */
+  public function setCollection(RouteCollection $collection);
+
+  /**
+   * Matches a request against multiple routes.
+   *
+   * @param Request $request
+   *   A Request object against which to match.
+   *
+   * @return array
+   *   An array of parameters
+   */
+  public function matchRequest(Request $request);
+}
diff --git a/core/lib/Drupal/Core/Routing/FirstEntryFinalMatcher.php b/core/lib/Drupal/Core/Routing/FirstEntryFinalMatcher.php
new file mode 100644
index 0000000..f1262df
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/FirstEntryFinalMatcher.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace Drupal\Core\Routing;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * Final matcher that simply returns the first item in the remaining routes.
+ *
+ * This class simply matches the first remaining route.
+ */
+class FirstEntryFinalMatcher implements FinalMatcherInterface {
+
+  /**
+   * The RouteCollection this matcher should match against.
+   *
+   * @var RouteCollection
+   */
+  protected $routes;
+
+  /**
+   * Sets the route collection this matcher should use.
+   *
+   * @param RouteCollection $collection
+   *   The collection against which to match.
+   *
+   * @return FinalMatcherInterface
+   *   The current matcher.
+   */
+  public function setCollection(RouteCollection $collection) {
+    $this->routes = $collection;
+
+    return $this;
+  }
+
+
+  public function matchRequest(Request $request) {
+    // Return whatever the first route in the collection is.
+    foreach ($this->routes as $name => $route) {
+      $path = '/' . $request->attributes->get('system_path');
+
+      $route->setOption('compiler_class', '\Drupal\Core\Routing\RouteCompiler');
+      $compiled = $route->compile();
+
+      preg_match($compiled->getRegex(), $path, $matches);
+
+      return array_merge($this->mergeDefaults($matches, $route->getDefaults()), array('_route' => $name));
+    }
+  }
+
+  /**
+   * Get merged default parameters.
+   *
+   * @param array $params
+   *  The parameters
+   * @param array $defaults
+   *   The defaults
+   *
+   * @return array
+   *   Merged default parameters
+   */
+  protected function mergeDefaults($params, $defaults) {
+    $parameters = $defaults;
+    foreach ($params as $key => $value) {
+      if (!is_int($key)) {
+        $parameters[$key] = $value;
+      }
+    }
+
+    return $parameters;
+  }
+
+}
+
diff --git a/core/lib/Drupal/Core/Routing/HttpMethodMatcher.php b/core/lib/Drupal/Core/Routing/HttpMethodMatcher.php
new file mode 100644
index 0000000..b0a1878
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/HttpMethodMatcher.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Drupal\Core\Routing;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+
+/**
+ * This class filters routes based on their HTTP Method.
+ */
+class HttpMethodMatcher extends PartialMatcher {
+
+  /**
+   * Matches a request against multiple routes.
+   *
+   * @param Request $request
+   *   A Request object against which to match.
+   *
+   * @return RouteCollection
+   *   A RouteCollection of matched routes.
+   */
+  public function matchRequestPartial(Request $request) {
+    $possible_methods = array();
+
+    $method = $request->getMethod();
+
+    $collection = new RouteCollection();
+
+    foreach ($this->routes->all() as $name => $route) {
+      // _method could be a |-delimited list of allowed methods, or null. If
+      // null, we accept any method.
+      $allowed_methods = array_filter(explode('|', strtoupper($route->getRequirement('_method'))));
+      if (empty($allowed_methods) || in_array($method, $allowed_methods)) {
+        $collection->add($name, $route);
+      }
+      else {
+        // Build a list of methods that would have matched. Note that we only
+        // need to do this if a route doesn't match, because if even one route
+        // passes then we'll never throw the exception that needs this array.
+        $possible_methods += $allowed_methods;
+      }
+    }
+
+    if (!count($collection->all())) {
+      throw new MethodNotAllowedException(array_unique($possible_methods));
+    }
+
+    return $collection;
+  }
+
+}
+
diff --git a/core/lib/Drupal/Core/Routing/InitialMatcherInterface.php b/core/lib/Drupal/Core/Routing/InitialMatcherInterface.php
new file mode 100644
index 0000000..a08cb12
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/InitialMatcherInterface.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Drupal\Core\Routing;
+
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * A PartialMatcher works like a UrlMatcher, but will return multiple candidate routes.
+ */
+interface InitialMatcherInterface {
+
+  /**
+   * Matches a request against multiple routes.
+   *
+   * @param Request $request
+   *   A Request object against which to match.
+   *
+   * @return RouteCollection
+   *   A RouteCollection of matched routes.
+   */
+  public function matchRequestPartial(Request $request);
+}
diff --git a/core/lib/Drupal/Core/Routing/MatcherDumper.php b/core/lib/Drupal/Core/Routing/MatcherDumper.php
new file mode 100644
index 0000000..5237fb1
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/MatcherDumper.php
@@ -0,0 +1,163 @@
+<?php
+
+namespace Drupal\Core\Routing;
+
+use Symfony\Component\Routing\Matcher\Dumper\MatcherDumperInterface;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+use Drupal\Core\Database\Connection;
+
+/**
+ * Dumps Route information to a database table.
+ */
+class MatcherDumper implements MatcherDumperInterface {
+
+  /**
+   * The maximum number of path elements for a route pattern;
+   */
+  const MAX_PARTS = 9;
+
+  /**
+   * The database connection to which to dump route information.
+   *
+   * @var Drupal\Core\Database\Connection
+   */
+  protected $connection;
+
+  /**
+   * The routes to be dumped.
+   *
+   * @var Symfony\Component\Routing\RouteCollection
+   */
+  protected $routes;
+
+  /**
+   * The name of the SQL table to which to dump the routes.
+   *
+   * @var string
+   */
+  protected $tableName;
+
+  public function __construct(Connection $connection, $table = 'router') {
+    $this->connection = $connection;
+
+    $this->tableName = $table;
+  }
+
+  /**
+   * Adds additional routes to be dumped.
+   *
+   * @param RouteCollection $routes
+   */
+  public function addRoutes(RouteCollection $routes) {
+    if (empty($this->routes)) {
+      $this->routes = $routes;
+    }
+    else {
+      $this->routes->addCollection($routes);
+    }
+  }
+
+  /**
+   * Dumps a set of routes to the router table in the database.
+   *
+   * Available options:
+   *
+   *  * route_set:  The route grouping that is being dumped. All existing
+   *     routes with this route set will be deleted on dump.
+   *  * base_class: The base class name
+   *
+   * @param $options array
+   *   $options An array of options
+   */
+  public function dump(array $options = array()) {
+    $options += array(
+      'route_set' => '',
+    );
+
+    //$compiled = $this->compileRoutes($this->routes, $route_set);
+
+    // Convert all of the routes into database records.
+    $insert = $this->connection->insert($this->tableName)->fields(array(
+      'name',
+      'route_set',
+      'fit',
+      'pattern',
+      'pattern_outline',
+      'number_parts',
+      'route',
+    ));
+
+    foreach ($this->routes as $name => $route) {
+      $route->setOption('compiler_class', '\Drupal\Core\Routing\RouteCompiler');
+      $compiled = $route->compile();
+      $values = array(
+        'name' => $name,
+        'route_set' => $options['route_set'],
+        'fit' => $compiled->getFit(),
+        'pattern' => $compiled->getPattern(),
+        'pattern_outline' => $compiled->getPatternOutline(),
+        'number_parts' => $compiled->getNumParts(),
+        // This is only temporary. We need to strip off the compiled route from
+        // route object in order to serialize it. Cloning strips off the
+        // compiled route object. Remove this once
+        // https://github.com/symfony/symfony/pull/4755 is merged and brought
+        // back downstream.
+       'route' => serialize(clone($route)),
+      );
+      $insert->values($values);
+    }
+
+    // Delete any old records in this route set first, then insert the new ones.
+    // That avoids stale data. The transaction makes it atomic to avoid
+    // unstable router states due to random failures.
+    $txn = $this->connection->startTransaction();
+
+    $this->connection->delete($this->tableName)
+      ->condition('route_set', $options['route_set'])
+      ->execute();
+
+    $insert->execute();
+
+    // We want to reuse the dumper for multiple route sets, so on dump, flush
+    // the queued routes.
+    $this->routes = NULL;
+
+    // Transaction ends here.
+  }
+
+  /**
+   * Gets the routes to match.
+   *
+   * @return RouteCollection
+   *   A RouteCollection instance representing all routes currently in the
+   *   dumper.
+   */
+  public function getRoutes() {
+    return $this->routes;
+  }
+
+  /**
+   * Determines the fitness of the provided path.
+   *
+   * @param string $path
+   *   The path whose fitness we want.
+   *
+   * @return int
+   *   The fitness of the path, as an integer.
+   */
+  public function getFit($path) {
+    $fit = 0;
+
+    $parts = explode('/', $path, static::MAX_PARTS);
+    foreach ($parts as $k => $part) {
+      if (strpos($part, '{') === FALSE) {
+        $fit |=  1 << ($slashes - $k);
+      }
+    }
+
+    return $fit;
+  }
+}
+
diff --git a/core/lib/Drupal/Core/Routing/NestedMatcher.php b/core/lib/Drupal/Core/Routing/NestedMatcher.php
new file mode 100644
index 0000000..db8a429
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/NestedMatcher.php
@@ -0,0 +1,149 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Routing\NestedMatcher.
+ */
+
+namespace Drupal\Core\Routing;
+
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * The nested matcher layers multiple partial matchers together.
+ */
+class NestedMatcher implements NestedMatcherInterface {
+
+  /**
+   * The final matcher
+   *
+   * @var RequestMatcherInterface
+   */
+  protected $finalMatcher;
+
+  /**
+   * An array of PartialMatchers.
+   *
+   * @var array
+   */
+  protected $partialMatchers = array();
+
+  /**
+   * The initial matcher to match against.
+   *
+   * @var InitialMatcherInterface
+   */
+  protected $initialMatcher;
+
+  /**
+   * The request context.
+   *
+   * @var RequestContext
+   */
+  protected $context;
+
+
+  /**
+   * Adds a partial matcher to the matching plan.
+   *
+   * Partial matchers will be run in the order in which they are added.
+   *
+   * @param PartialMatcherInterface $matcher
+   *   A partial
+   *
+   * @return NestedMatcherInterface
+   *   The current matcher.
+   */
+  public function addPartialMatcher(PartialMatcherInterface $matcher) {
+    $this->partialMatchers[] = $matcher;
+
+    return $this;
+  }
+
+  /**
+   * Sets the final matcher for the matching plan.
+   *
+   * @param UrlMatcherInterface $final
+   *   The matcher that will be called last to ensure only a single route is
+   *   found.
+   *
+   * @return NestedMatcherInterface
+   *   The current matcher.
+   */
+  public function setFinalMatcher(FinalMatcherInterface $final) {
+    $this->finalMatcher = $final;
+
+    return $this;
+  }
+
+  /**
+   * Sets the first matcher for the matching plan.
+   *
+   * Partial matchers will be run in the order in which they are added.
+   *
+   * @param InitialMatcherInterface $matcher
+   *   An initial matcher.  It is responsible for its own configuration and
+   *   initial route collection
+   *
+   * @return NestedMatcherInterface
+   *   The current matcher.
+   */
+  public function setInitialMatcher(InitialMatcherInterface $initial) {
+    $this->initialMatcher = $initial;
+
+    return $this;
+  }
+
+  /**
+    * Tries to match a request with a set of routes.
+    *
+    * If the matcher can not find information, it must throw one of the exceptions documented
+    * below.
+    *
+    * @param Request $request The request to match
+    *
+    * @return array An array of parameters
+    *
+    * @throws ResourceNotFoundException If no matching resource could be found
+    * @throws MethodNotAllowedException If a matching resource was found but the request method is not allowed
+    */
+  public function matchRequest(Request $request) {
+    $collection = $this->initialMatcher->matchRequestPartial($request);
+
+    foreach ($this->partialMatchers as $matcher) {
+      if ($collection) {
+        $matcher->setCollection($collection);
+      }
+      $collection = $matcher->matchRequestPartial($request);
+    }
+
+    $attributes = $this->finalMatcher->setCollection($collection)->matchRequest($request);
+
+    return $attributes;
+  }
+
+  /**
+   * Sets the request context.
+   *
+   * This method is unused. It is here only to satisfy the interface.
+   *
+   * @param RequestContext $context The context
+   */
+  public function setContext(RequestContext $context) {
+    $this->context = $context;
+  }
+
+  /**
+   * Gets the request context.
+   *
+   * This method is unused. It is here only to satisfy the interface.
+   *
+   * @return RequestContext The context
+   */
+  public function getContext() {
+    return $this->context;
+  }
+
+}
+
diff --git a/core/lib/Drupal/Core/Routing/NestedMatcherInterface.php b/core/lib/Drupal/Core/Routing/NestedMatcherInterface.php
new file mode 100644
index 0000000..cd55d32
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/NestedMatcherInterface.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace Drupal\Core\Routing;
+
+use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
+
+/**
+ * A NestedMatcher allows for multiple-stage resolution of a route.
+ */
+interface NestedMatcherInterface extends RequestMatcherInterface {
+
+  /**
+   * Sets the first matcher for the matching plan.
+   *
+   * Partial matchers will be run in the order in which they are added.
+   *
+   * @param InitialMatcherInterface $matcher
+   *   An initial matcher.  It is responsible for its own configuration and
+   *   initial route collection
+   *
+   * @return NestedMatcherInterface
+   *   The current matcher.
+   */
+  public function setInitialMatcher(InitialMatcherInterface $initial);
+
+  /**
+   * Adds a partial matcher to the matching plan.
+   *
+   * Partial matchers will be run in the order in which they are added.
+   *
+   * @param PartialMatcherInterface $matcher
+   *   A partial matcher.
+   *
+   * @return NestedMatcherInterface
+   *   The current matcher.
+   */
+  public function addPartialMatcher(PartialMatcherInterface $matcher);
+
+  /**
+   * Sets the final matcher for the matching plan.
+   *
+   * @param FinalMatcherInterface $final
+   *   The matcher that will be called last to ensure only a single route is
+   *   found.
+   *
+   * @return NestedMatcherInterface
+   *   The current matcher.
+   */
+  public function setFinalMatcher(FinalMatcherInterface $final);
+}
diff --git a/core/lib/Drupal/Core/Routing/PartialMatcher.php b/core/lib/Drupal/Core/Routing/PartialMatcher.php
new file mode 100644
index 0000000..775bb3a
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/PartialMatcher.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Drupal\Core\Routing;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * Utility base class for partial matchers.
+ */
+abstract class PartialMatcher implements PartialMatcherInterface {
+
+  /**
+   * The RouteCollection this matcher should match against.
+   *
+   * @var RouteCollection
+   */
+  protected $routes;
+
+  /**
+   * Sets the route collection this matcher should use.
+   *
+   * @param RouteCollection $collection
+   *   The collection against which to match.
+   *
+   * @return PartialMatcherInterface
+   *   The current matcher.
+   */
+  public function setCollection(RouteCollection $collection) {
+    $this->routes = $collection;
+
+    return $this;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Routing/PartialMatcherInterface.php b/core/lib/Drupal/Core/Routing/PartialMatcherInterface.php
new file mode 100644
index 0000000..1b234e8
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/PartialMatcherInterface.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Drupal\Core\Routing;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * A PartialMatcher works like a UrlMatcher, but will return multiple candidate routes.
+ */
+interface PartialMatcherInterface {
+
+  /**
+   * Sets the route collection this matcher should use.
+   *
+   * @param RouteCollection $collection
+   *   The collection against which to match.
+   *
+   * @return PartialMatcherInterface
+   *   The current matcher.
+   */
+  public function setCollection(RouteCollection $collection);
+
+  /**
+   * Matches a request against multiple routes.
+   *
+   * @param Request $request
+   *   A Request object against which to match.
+   *
+   * @return RouteCollection
+   *   A RouteCollection of matched routes.
+   */
+  public function matchRequestPartial(Request $request);
+}
diff --git a/core/lib/Drupal/Core/Routing/PathMatcher.php b/core/lib/Drupal/Core/Routing/PathMatcher.php
new file mode 100644
index 0000000..8930932
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/PathMatcher.php
@@ -0,0 +1,133 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Routing\PathMatcher.
+ */
+
+namespace Drupal\Core\Routing;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+
+use Drupal\Core\Database\Connection;
+
+/**
+ * Initial matcher to match a route against a built database, by path.
+ */
+class PathMatcher implements InitialMatcherInterface {
+  /**
+   * The database connection from which to read route information.
+   *
+   * @var Drupal\Core\Database\Connection
+   */
+  protected $connection;
+
+  /**
+   * The name of the SQL table from which to read the routes.
+   *
+   * @var string
+   */
+  protected $tableName;
+
+  /**
+   * Constructs a new PathMatcher.
+   *
+   * @param \Drupal\Core\Database\Connection $connection
+   *   A database connection object.
+   * @param string $table
+   *   The table in the database to use for matching.
+   */
+  public function __construct(Connection $connection, $table = 'router') {
+    $this->connection = $connection;
+    $this->tableName = $table;
+  }
+
+  /**
+   * Matches a request against multiple routes.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   A Request object against which to match.
+   *
+   * @return \Symfony\Component\Routing\RouteCollection
+   *   A RouteCollection of matched routes.
+   */
+  public function matchRequestPartial(Request $request) {
+
+    $path = rtrim($request->getPathInfo(), '/');
+
+    $parts = array_slice(array_filter(explode('/', $path)), 0, MatcherDumper::MAX_PARTS);
+
+    $ancestors = $this->getCandidateOutlines($parts);
+
+    $routes = $this->connection->query("SELECT name, route FROM {{$this->tableName}} WHERE pattern_outline IN (:patterns) ORDER BY fit", array(
+      ':patterns' => $ancestors,
+    ))
+    ->fetchAllKeyed();
+
+    $collection = new RouteCollection();
+    foreach ($routes as $name => $route) {
+      $route = unserialize($route);
+      if (preg_match($route->compile()->getRegex(), $path, $matches)) {
+        $collection->add($name, $route);
+      }
+    }
+
+    if (!count($collection->all())) {
+      throw new ResourceNotFoundException();
+    }
+
+    return $collection;
+  }
+
+  /**
+   * Returns an array of path pattern outlines that could match the path parts.
+   *
+   * @param array $parts
+   *   The parts of the path for which we want candidates.
+   * @return array
+   *   An array of outlines that could match the specified path parts.
+   */
+  public function getCandidateOutlines(array $parts) {
+    $number_parts = count($parts);
+    $ancestors = array();
+    $length =  $number_parts - 1;
+    $end = (1 << $number_parts) - 1;
+
+    // The highest possible mask is a 1 bit for every part of the path. We will
+    // check every value down from there to generate a possible outline.
+    $masks = range($end, pow($number_parts - 1, 2));
+
+    // Only examine patterns that actually exist as router items (the masks).
+    foreach ($masks as $i) {
+      if ($i > $end) {
+        // Only look at masks that are not longer than the path of interest.
+        continue;
+      }
+      elseif ($i < (1 << $length)) {
+        // We have exhausted the masks of a given length, so decrease the length.
+        --$length;
+      }
+      $current = '';
+      for ($j = $length; $j >= 0; $j--) {
+        // Check the bit on the $j offset.
+        if ($i & (1 << $j)) {
+          // Bit one means the original value.
+          $current .= $parts[$length - $j];
+        }
+        else {
+          // Bit zero means means wildcard.
+          $current .= '%';
+        }
+        // Unless we are at offset 0, add a slash.
+        if ($j) {
+          $current .= '/';
+        }
+      }
+      $ancestors[] = '/' . $current;
+    }
+    return $ancestors;
+  }
+}
+
diff --git a/core/lib/Drupal/Core/Routing/RouteBuilder.php b/core/lib/Drupal/Core/Routing/RouteBuilder.php
new file mode 100644
index 0000000..a1d8daf
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/RouteBuilder.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Drupal\Core\Routing;
+
+use Symfony\Component\Routing\RouteCompilerInterface;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\Matcher\Dumper\MatcherDumperInterface;
+
+/**
+ * Managing class for rebuilding the router table.
+ *
+ * Because this class makes use of the modules system, it cannot currently
+ * be unit tested.
+ */
+class RouteBuilder {
+
+  protected $dumper;
+
+  public function __construct(MatcherDumperInterface $dumper) {
+    $this->dumper = $dumper;
+  }
+
+  public function rebuild() {
+    // We need to manually call each module so that we can know which module
+    // a given item came from.
+
+    foreach (module_implements('route_info') as $module) {
+      $routes = call_user_func($module . '_route_info');
+      drupal_alter('router_info', $routes, $module);
+      $this->dumper->addRoutes($routes);
+      $this->dumper->dump(array('route_set' => $module));
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Routing/RouteCompiler.php b/core/lib/Drupal/Core/Routing/RouteCompiler.php
new file mode 100644
index 0000000..0564a45
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/RouteCompiler.php
@@ -0,0 +1,239 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Routing\RouteCompiler.
+ */
+
+namespace Drupal\Core\Routing;
+
+use Symfony\Component\Routing\RouteCompilerInterface;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Compiler to generate derived information from a Route necessary for matching.
+ */
+class RouteCompiler implements RouteCompilerInterface {
+
+  /**
+   * The maximum number of path elements for a route pattern;
+   */
+  const MAX_PARTS = 9;
+
+  /**
+   * Utility constant to use for regular expressions against the path.
+*/
+  const REGEX_DELIMITER = '#';
+
+  /**
+    * Compiles the current route instance.
+    *
+    * @param \Symfony\Component\Routing\Route $route
+    *   A Route instance
+    *
+    * @return CompiledRoute
+    *   A CompiledRoute instance
+    */
+  public function compile(Route $route) {
+
+    $stripped_path = $this->getPathWithoutDefaults($route);
+
+    $fit = $this->getFit($stripped_path);
+
+    $pattern_outline = $this->getPatternOutline($stripped_path);
+
+    $num_parts = count(explode('/', trim($pattern_outline, '/')));
+
+    $regex = $this->getRegex($route, $route->getPattern());
+
+    return new CompiledRoute($route, $fit, $pattern_outline, $num_parts, $regex);
+  }
+
+  /**
+   * Generates a regular expression that will match this pattern.
+   *
+   * This regex can be used in preg_match() to extract values inside {}.
+   *
+   * This algorithm was lifted directly from Symfony's RouteCompiler class.
+   * It is not factored out nicely there, so we cannot simply subclass it.
+   * @todo Refactor Symfony's RouteCompiler so that it's useful to subclass.
+   *
+   * @param \Symfony\Component\Routing\Route $route
+   *   The route object.
+   * @param string $pattern
+   *   The pattern for which we want a matching regex.
+   * @return type
+   * @throws \LogicException
+   */
+  public function getRegex(Route $route, $pattern) {
+    $len = strlen($pattern);
+    $tokens = array();
+    $variables = array();
+    $pos = 0;
+    preg_match_all('#.\{(\w+)\}#', $pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
+    foreach ($matches as $match) {
+      if ($text = substr($pattern, $pos, $match[0][1] - $pos)) {
+        $tokens[] = array('text', $text);
+      }
+
+      $pos = $match[0][1] + strlen($match[0][0]);
+      $var = $match[1][0];
+
+      if ($req = $route->getRequirement($var)) {
+        $regexp = $req;
+      }
+      else {
+        // Use the character preceding the variable as a separator
+        $separators = array($match[0][0][0]);
+
+        if ($pos !== $len) {
+          // Use the character following the variable as the separator when available
+          $separators[] = $pattern[$pos];
+        }
+        $regexp = sprintf('[^%s]+', preg_quote(implode('', array_unique($separators)), self::REGEX_DELIMITER));
+      }
+
+      $tokens[] = array('variable', $match[0][0][0], $regexp, $var);
+
+      if (in_array($var, $variables)) {
+        throw new \LogicException(sprintf('Route pattern "%s" cannot reference variable name "%s" more than once.', $route->getPattern(), $var));
+      }
+
+      $variables[] = $var;
+    }
+
+    if ($pos < $len) {
+      $tokens[] = array('text', substr($pattern, $pos));
+    }
+
+    // find the first optional token
+    $firstOptional = INF;
+    for ($i = count($tokens) - 1; $i >= 0; $i--) {
+        $token = $tokens[$i];
+        if ('variable' === $token[0] && $route->hasDefault($token[3])) {
+            $firstOptional = $i;
+        } else {
+            break;
+        }
+    }
+
+    // compute the matching regexp
+    $regexp = '';
+    for ($i = 0, $nbToken = count($tokens); $i < $nbToken; $i++) {
+        $regexp .= $this->computeRegexp($tokens, $i, $firstOptional);
+    }
+
+    return self::REGEX_DELIMITER.'^'.$regexp.'$'.self::REGEX_DELIMITER.'s';
+  }
+
+  /**
+    * Computes the regexp used to match a specific token. It can be static text or a subpattern.
+    *
+    * @param array   $tokens        The route tokens
+    * @param integer $index         The index of the current token
+    * @param integer $firstOptional The index of the first optional token
+    *
+    * @return string The regexp pattern for a single token
+    */
+  private function computeRegexp(array $tokens, $index, $firstOptional) {
+      $token = $tokens[$index];
+      if ('text' === $token[0]) {
+          // Text tokens
+          return preg_quote($token[1], self::REGEX_DELIMITER);
+      } else {
+          // Variable tokens
+          if (0 === $index && 0 === $firstOptional) {
+              // When the only token is an optional variable token, the separator is required
+              return sprintf('%s(?<%s>%s)?', preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]);
+          } else {
+              $regexp = sprintf('%s(?<%s>%s)', preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]);
+              if ($index >= $firstOptional) {
+                  // Enclose each optional token in a subpattern to make it optional.
+                  // "?:" means it is non-capturing, i.e. the portion of the subject string that
+                  // matched the optional subpattern is not passed back.
+                  $regexp = "(?:$regexp";
+                  $nbTokens = count($tokens);
+                  if ($nbTokens - 1 == $index) {
+                      // Close the optional subpatterns
+                      $regexp .= str_repeat(")?", $nbTokens - $firstOptional - (0 === $firstOptional ? 1 : 0));
+                  }
+              }
+
+              return $regexp;
+          }
+      }
+  }
+
+
+  /**
+   * Returns the pattern outline.
+   *
+   * The pattern outline is the path pattern but normalized so that all
+   * placeholders are equal strings and default values are removed.
+   *
+   * @param string $path
+   *   The path for which we want the normalized outline.
+   *
+   * @return string
+   *   The path pattern outline.
+   */
+  public function getPatternOutline($path) {
+    return preg_replace('#\{\w+\}#', '%', $path);
+  }
+
+  /**
+   * Determines the fitness of the provided path.
+   *
+   * @param string $path
+   *   The path whose fitness we want.
+   *
+   * @return int
+   *   The fitness of the path, as an integer.
+   */
+  public function getFit($path) {
+    $parts = explode('/', trim($path, '/'), static::MAX_PARTS);
+    $number_parts = count($parts);
+    // We store the highest index of parts here to save some work in the fit
+    // calculation loop.
+    $slashes = $number_parts - 1;
+
+    $fit = 0;
+    foreach ($parts as $k => $part) {
+      if (strpos($part, '{') === FALSE) {
+        $fit |=  1 << ($slashes - $k);
+      }
+    }
+
+    return $fit;
+  }
+
+  /**
+   * Returns the path of the route, without placeholders with a default value.
+   *
+   * When computing the path outline and fit, we want to skip default-value
+   * placeholders.  If we didn't, the path would never match.  Note that this
+   * only works for placeholders at the end of the path. Infix placeholders
+   * with default values don't make sense anyway, so that should not be a
+   * problem.
+   *
+   * @param \Symfony\Component\Routing\Route $route
+   *
+   * @return string
+   *   The path string, stripped of placeholders that have default values.
+   */
+  protected function getPathWithoutDefaults(Route $route) {
+    $path = $route->getPattern();
+    $defaults = $route->getDefaults();
+
+    // Remove placeholders with default values from the outline, so that they
+    // will still match.
+    $remove = array_map(function($a) {
+      return '/{' . $a . '}';
+    }, array_keys($defaults));
+    $path = str_replace($remove, '', $path);
+
+    return $path;
+  }
+
+}
+
diff --git a/core/modules/system/lib/Drupal/system/FileDownload.php b/core/modules/system/lib/Drupal/system/FileDownload.php
new file mode 100644
index 0000000..b55872c
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/FileDownload.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace Drupal\system;
+
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+
+/**
+ * Controller class for private file downloads.
+ */
+class FileDownload {
+
+  /**
+  * Page callback: Handles private file transfers.
+  *
+  * Call modules that implement hook_file_download() to find out if a file is
+  * accessible and what headers it should be transferred with. If one or more
+  * modules returned headers the download will start with the returned headers.
+  * If a module returns -1 an AccessDeniedHttpException will be thrown.
+  * If the file exists but no modules responded an AccessDeniedHttpException will
+  * be thrown.If the file does not exist a NotFoundHttpException will be thrown.
+  *
+  * @see hook_file_download()
+  */
+  public function download() {
+    // Merge remaining path arguments into relative file path.
+    $args = func_get_args();
+    $scheme = array_shift($args);
+    $target = implode('/', $args);
+    $uri = $scheme . '://' . $target;
+    if (file_stream_wrapper_valid_scheme($scheme) && file_exists($uri)) {
+      // Let other modules provide headers and controls access to the file.
+      // module_invoke_all() uses array_merge_recursive() which merges header
+      // values into a new array. To avoid that and allow modules to override
+      // headers instead, use array_merge() to merge the returned arrays.
+      $headers = array();
+      foreach (module_implements('file_download') as $module) {
+        $function = $module . '_file_download';
+        $result = $function($uri);
+        if ($result == -1) {
+          throw new AccessDeniedHttpException();
+        }
+        if (isset($result) && is_array($result)) {
+          $headers = array_merge($headers, $result);
+        }
+      }
+      if (count($headers)) {
+        return file_transfer($uri, $headers);
+      }
+      throw new AccessDeniedHttpException();
+    }
+    throw new NotFoundHttpException();
+  }
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/RouterTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/RouterTest.php
index 42e81b1..d8950ce 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Menu/RouterTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Menu/RouterTest.php
@@ -178,7 +178,7 @@ class RouterTest extends WebTestBase {
 
     $this->drupalGet('user/login');
     // Check that we got to 'user'.
-    $this->assertTrue($this->url == url('user', array('absolute' => TRUE)), t("Logged-in user redirected to user on accessing user/login"));
+    $this->assertTrue($this->url == url('user/' . $web_user->uid, array('absolute' => TRUE)), t("Logged-in user redirected to user on accessing user/login"));
 
     // user/register should redirect to user/UID/edit.
     $this->drupalGet('user/register');
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/ChainMatcherTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/ChainMatcherTest.php
new file mode 100644
index 0000000..cec4d79
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Routing/ChainMatcherTest.php
@@ -0,0 +1,111 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\system\Tests\Routing\ChainMatcherTest.
+ */
+
+namespace Drupal\system\Tests\Routing;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\Exception\RouteNotFoundException;
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+
+use Drupal\simpletest\UnitTestBase;
+use Drupal\Core\Routing\ChainMatcher;
+
+use Exception;
+
+/**
+ * Basic tests for the ChainMatcher.
+ */
+class ChainMatcherTest extends UnitTestBase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Chain matcher tests',
+      'description' => 'Confirm that the chain matcher is working correctly.',
+      'group' => 'Routing',
+    );
+  }
+
+  /**
+   * Confirms that the expected exception is thrown.
+   */
+  public function testMethodNotAllowed() {
+
+    $chain = new ChainMatcher();
+
+    $method_not_allowed = new MockMatcher(function(Request $request) {
+      throw new MethodNotAllowedException(array('POST'));
+    });
+
+    try {
+      $chain->add($method_not_allowed);
+      $chain->matchRequest(Request::create('my/path'));
+    }
+    catch (MethodNotAllowedException $e) {
+      $this->pass('Correct exception thrown.');
+    }
+    catch (Exception $e) {
+      $this->fail('Incorrect exception thrown: ' . get_class($e));
+    }
+  }
+
+  /**
+   * Confirms that the expected exception is thrown.
+   */
+  public function testRequestNotFound() {
+
+    $chain = new ChainMatcher();
+
+    $resource_not_found = new MockMatcher(function(Request $request) {
+      throw new ResourceNotFoundException();
+    });
+
+    try {
+      $chain->add($resource_not_found);
+      $chain->matchRequest(Request::create('my/path'));
+    }
+    catch (ResourceNotFoundException $e) {
+      $this->pass('Correct exception thrown.');
+    }
+    catch (Exception $e) {
+      $this->fail('Incorrect exception thrown: ' . get_class($e));
+    }
+  }
+
+  /**
+   * Confirms that the expected exception is thrown.
+   */
+  public function testRequestFound() {
+
+    $chain = new ChainMatcher();
+
+    $method_not_allowed = new MockMatcher(function(Request $request) {
+      throw new MethodNotAllowedException(array('POST'));
+    });
+
+    $resource_not_found = new MockMatcher(function(Request $request) {
+      throw new ResourceNotFoundException();
+    });
+
+    $found_data = new MockMatcher(function(Request $request) {
+      return array('_controller' => 'foo');
+    });
+
+    try {
+      $chain->add($method_not_allowed);
+      $chain->add($resource_not_found);
+      $chain->add($found_data);
+      $request = Request::create('my/path');
+      $attributes = $chain->matchRequest($request);
+      $this->assertEqual($attributes['_controller'], 'foo', 'Correct attributes returned.');
+    }
+    catch (Exception $e) {
+      $this->fail('Exception thrown when a match should have been successful: ' . get_class($e));
+    }
+  }
+
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/FirstEntryFinalMatcherTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/FirstEntryFinalMatcherTest.php
new file mode 100644
index 0000000..0f17477
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Routing/FirstEntryFinalMatcherTest.php
@@ -0,0 +1,116 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\system\Tests\Routing\NestedMatcherTest.
+ */
+
+namespace Drupal\system\Tests\Routing;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+
+use Drupal\simpletest\UnitTestBase;
+use Drupal\Core\Routing\HttpMethodMatcher;
+use Drupal\Core\Routing\NestedMatcher;
+use Drupal\Core\Routing\FirstEntryFinalMatcher;
+
+use Exception;
+
+/**
+ * Basic tests for the NestedMatcher class.
+ */
+class FirstEntryFinalMatcherTest extends UnitTestBase {
+
+  /**
+   * A collection of shared fixture data for tests.
+   *
+   * @var RoutingFixtures
+   */
+  protected $fixtures;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'FirstEntryFinalMatcher tests',
+      'description' => 'Confirm that the FirstEntryFinalMatcher is working properly.',
+      'group' => 'Routing',
+    );
+  }
+
+  function __construct($test_id = NULL) {
+    parent::__construct($test_id);
+
+    $this->fixtures = new RoutingFixtures();
+  }
+  public function setUp() {
+    parent::setUp();
+  }
+
+  /**
+   * Confirms the final matcher returns correct attributes for static paths.
+   */
+  public function testFinalMatcherStatic() {
+
+    $collection = new RouteCollection();
+    $collection->add('route_a', new Route('/path/one', array(
+      '_controller' => 'foo',
+    )));
+
+    $request = Request::create('/path/one', 'GET');
+
+    $matcher = new FirstEntryFinalMatcher();
+    $matcher->setCollection($collection);
+    $attributes = $matcher->matchRequest($request);
+
+    $this->assertEqual($attributes['_route'], 'route_a', 'The correct matching route was found.');
+    $this->assertEqual($attributes['_controller'], 'foo', 'The correct controller was found.');
+  }
+
+  /**
+   * Confirms the final matcher returns correct attributes for pattern paths.
+   */
+  public function testFinalMatcherPattern() {
+
+    $collection = new RouteCollection();
+    $collection->add('route_a', new Route('/path/one/{value}', array(
+      '_controller' => 'foo',
+    )));
+
+    $request = Request::create('/path/one/narf', 'GET');
+    $request->attributes->set('system_path', 'path/one/narf');
+
+    $matcher = new FirstEntryFinalMatcher();
+    $matcher->setCollection($collection);
+    $attributes = $matcher->matchRequest($request);
+
+    $this->assertEqual($attributes['_route'], 'route_a', 'The correct matching route was found.');
+    $this->assertEqual($attributes['_controller'], 'foo', 'The correct controller was found.');
+    $this->assertEqual($attributes['value'], 'narf', 'Required placeholder value found.');
+  }
+
+  /**
+   * Confirms the final matcher returns correct attributes with default values.
+   */
+  public function testFinalMatcherPatternDefalts() {
+
+    $collection = new RouteCollection();
+    $collection->add('route_a', new Route('/path/one/{value}', array(
+      '_controller' => 'foo',
+      'value' => 'poink'
+    )));
+
+    $request = Request::create('/path/one', 'GET');
+    $request->attributes->set('system_path', 'path/one');
+
+    $matcher = new FirstEntryFinalMatcher();
+    $matcher->setCollection($collection);
+    $attributes = $matcher->matchRequest($request);
+
+    $this->assertEqual($attributes['_route'], 'route_a', 'The correct matching route was found.');
+    $this->assertEqual($attributes['_controller'], 'foo', 'The correct controller was found.');
+    $this->assertEqual($attributes['value'], 'poink', 'Optional placeholder value used default.');
+  }
+}
+
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/HttpMethodMatcherTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/HttpMethodMatcherTest.php
new file mode 100644
index 0000000..962abfc
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Routing/HttpMethodMatcherTest.php
@@ -0,0 +1,109 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\system\Tests\Routing\HttpMethodMMatcherTest.
+ */
+
+namespace Drupal\system\Tests\Routing;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+
+use Drupal\simpletest\UnitTestBase;
+use Drupal\Core\Routing\HttpMethodMatcher;
+use Drupal\Core\Routing\NestedMatcher;
+use Drupal\Core\Routing\FirstEntryFinalMatcher;
+
+use Exception;
+
+/**
+ * Basic tests for the HttpMethodMatcher class.
+ */
+class HttpMethodMatcherTest extends UnitTestBase {
+
+  /**
+   * A collection of shared fixture data for tests.
+   *
+   * @var RoutingFixtures
+   */
+  protected $fixtures;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Partial matcher HTTP Method tests',
+      'description' => 'Confirm that the Http Method partial matcher is functioning properly.',
+      'group' => 'Routing',
+    );
+  }
+
+  function __construct($test_id = NULL) {
+    parent::__construct($test_id);
+
+    $this->fixtures = new RoutingFixtures();
+  }
+  public function setUp() {
+    parent::setUp();
+  }
+
+  /**
+   * Confirms that the HttpMethod matcher matches properly.
+   */
+  public function testFilterRoutes() {
+
+    $matcher = new HttpMethodMatcher();
+    $matcher->setCollection($this->fixtures->sampleRouteCollection());
+
+    $routes = $matcher->matchRequestPartial(Request::create('path/one', 'GET'));
+
+    $this->assertEqual(count($routes->all()), 4, 'The correct number of routes was found.');
+    $this->assertNotNull($routes->get('route_a'), 'The first matching route was found.');
+    $this->assertNull($routes->get('route_b'), 'The non-matching route was not found.');
+    $this->assertNotNull($routes->get('route_c'), 'The second matching route was found.');
+    $this->assertNotNull($routes->get('route_d'), 'The all-matching route was found.');
+    $this->assertNotNull($routes->get('route_e'), 'The multi-matching route was found.');
+  }
+
+  /**
+   * Confirms we can nest multiple partial matchers.
+   */
+  public function testNestedMatcher() {
+
+    $matcher = new NestedMatcher();
+
+    $matcher->setInitialMatcher(new MockPathMatcher($this->fixtures->sampleRouteCollection()));
+    $matcher->addPartialMatcher(new HttpMethodMatcher());
+    $matcher->setFinalMatcher(new FirstEntryFinalMatcher());
+
+    $request = Request::create('/path/one', 'GET');
+
+    $attributes = $matcher->matchRequest($request);
+
+    $this->assertEqual($attributes['_route'], 'route_a', 'The correct matching route was found.');
+  }
+
+  /**
+   * Confirms that the HttpMethod matcher throws an exception for no-route.
+   */
+  public function testNoRouteFound() {
+    $matcher = new HttpMethodMatcher();
+
+    // Remove the sample route that would match any method.
+    $routes = $this->fixtures->sampleRouteCollection();
+    $routes->remove('route_d');
+
+    $matcher->setCollection($routes);
+
+    try {
+      $routes = $matcher->matchRequestPartial(Request::create('path/one', 'DELETE'));
+      $this->fail(t('No exception was thrown.'));
+    }
+    catch (Exception $e) {
+      $this->assertTrue($e instanceof MethodNotAllowedException, 'The correct exception was thrown.');
+    }
+
+  }
+}
+
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/MatcherDumperTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/MatcherDumperTest.php
new file mode 100644
index 0000000..7f6f312
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Routing/MatcherDumperTest.php
@@ -0,0 +1,144 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\system\Tests\Routing\UrlMatcherDumperTest.
+ */
+
+namespace Drupal\system\Tests\Routing;
+
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+use Drupal\simpletest\UnitTestBase;
+use Drupal\Core\Database\Database;
+use Drupal\Core\Routing\MatcherDumper;
+
+/**
+ * Basic tests for the UrlMatcherDumper.
+ */
+class MatcherDumperTest extends UnitTestBase {
+
+  /**
+   * A collection of shared fixture data for tests.
+   *
+   * @var RoutingFixtures
+   */
+  protected $fixtures;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Dumper tests',
+      'description' => 'Confirm that the matcher dumper is functioning properly.',
+      'group' => 'Routing',
+    );
+  }
+
+  function __construct($test_id = NULL) {
+    parent::__construct($test_id);
+
+    $this->fixtures = new RoutingFixtures();
+  }
+
+  function setUp() {
+    parent::setUp();
+  }
+
+  /**
+   * Confirms that the dumper can be instantiated successfuly.
+   */
+  function testCreate() {
+    $connection = Database::getConnection();
+    $dumper= new MatcherDumper($connection);
+
+    $class_name = 'Drupal\Core\Routing\MatcherDumper';
+    $this->assertTrue($dumper instanceof $class_name, 'Dumper created successfully');
+  }
+
+  /**
+   * Confirms that we can add routes to the dumper.
+   */
+  function testAddRoutes() {
+    $connection = Database::getConnection();
+    $dumper= new MatcherDumper($connection);
+
+    $route = new Route('test');
+    $collection = new RouteCollection();
+    $collection->add('test_route', $route);
+
+    $dumper->addRoutes($collection);
+
+    $dumper_routes = $dumper->getRoutes()->all();
+    $collection_routes = $collection->all();
+
+    foreach ($dumper_routes as $name => $route) {
+      $this->assertEqual($route->getPattern(), $collection_routes[$name]->getPattern(), 'Routes match');
+    }
+  }
+
+  /**
+   * Confirms that we can add routes to the dumper when it already has some.
+   */
+  function testAddAdditionalRoutes() {
+    $connection = Database::getConnection();
+    $dumper= new MatcherDumper($connection);
+
+    $route = new Route('test');
+    $collection = new RouteCollection();
+    $collection->add('test_route', $route);
+    $dumper->addRoutes($collection);
+
+    $route = new Route('test2');
+    $collection2 = new RouteCollection();
+    $collection2->add('test_route2', $route);
+    $dumper->addRoutes($collection2);
+
+    // Merge the two collections together so we can test them.
+    $collection->addCollection(clone $collection2);
+
+    $dumper_routes = $dumper->getRoutes()->all();
+    $collection_routes = $collection->all();
+
+    $success = TRUE;
+    foreach ($collection_routes as $name => $route) {
+      if (empty($dumper_routes[$name])) {
+        $success = FALSE;
+        $this->fail(t('Not all routes found in the dumper.'));
+      }
+    }
+
+    if ($success) {
+      $this->pass('All routes found in the dumper.');
+    }
+  }
+
+  /**
+   * Confirm that we can dump a route collection to the database.
+   */
+  public function testDump() {
+    $connection = Database::getConnection();
+    $dumper= new MatcherDumper($connection, 'test_routes');
+
+    $route = new Route('/test/{my}/path');
+    $route->setOption('compiler_class', 'Drupal\Core\Routing\RouteCompiler');
+    $collection = new RouteCollection();
+    $collection->add('test_route', $route);
+
+    $dumper->addRoutes($collection);
+
+    $this->fixtures->createTables($connection);
+
+    $dumper->dump(array('route_set' => 'test'));
+
+    $record = $connection->query("SELECT * FROM {test_routes} WHERE name= :name", array(':name' => 'test_route'))->fetchObject();
+
+    $loaded_route = unserialize($record->route);
+
+    $this->assertEqual($record->name, 'test_route', 'Dumped route has correct name.');
+    $this->assertEqual($record->pattern, '/test/{my}/path', 'Dumped route has correct pattern.');
+    $this->assertEqual($record->pattern_outline, '/test/%/path', 'Dumped route has correct pattern outline.');
+    $this->assertEqual($record->fit, 5 /* 101 in binary */, 'Dumped route has correct fit.');
+    $this->assertTrue($loaded_route instanceof Route, 'Route object retrieved successfully.');
+
+  }
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/MockMatcher.php b/core/modules/system/lib/Drupal/system/Tests/Routing/MockMatcher.php
new file mode 100644
index 0000000..6cd58fc
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Routing/MockMatcher.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\system\Tests\Routing\MockMatcher.
+ */
+
+namespace Drupal\system\Tests\Routing;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\Exception\RouteNotFoundException;
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+
+use Closure;
+
+/**
+ * A mock matcher that can be configured with any matching logic for testing.
+ *
+ */
+class MockMatcher implements RequestMatcherInterface {
+
+  protected $matcher;
+
+  public function __construct(Closure $matcher) {
+    $this->matcher = $matcher;
+  }
+
+  public function matchRequest(Request $request) {
+    $matcher = $this->matcher;
+    return $matcher($request);
+  }
+}
+
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/MockPathMatcher.php b/core/modules/system/lib/Drupal/system/Tests/Routing/MockPathMatcher.php
new file mode 100644
index 0000000..b545ebe
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Routing/MockPathMatcher.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace Drupal\system\Tests\Routing;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+use Drupal\Core\Routing\InitialMatcherInterface;
+
+/**
+ * Description of MockPathMatcher
+ *
+ * @author crell
+ */
+class MockPathMatcher implements InitialMatcherInterface {
+
+  protected $routes;
+
+  public function __construct(RouteCollection $routes) {
+    $this->routes = $routes;
+  }
+
+  /**
+   * Matches a request against multiple routes.
+   *
+   * @param Request $request
+   *   A Request object against which to match.
+   *
+   * @return RouteCollection
+   *   A RouteCollection of matched routes.
+   */
+  public function matchRequestPartial(Request $request) {
+    // For now for testing we'll just do a straight string match.
+
+    $path = $request->getPathInfo();
+
+    $return = new RouteCollection();
+
+    foreach ($this->routes as $name => $route) {
+      if ($route->getPattern() == $path) {
+        $return->add($name, $route);
+      }
+    }
+
+    return $return;
+  }
+
+
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/NestedMatcherTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/NestedMatcherTest.php
new file mode 100644
index 0000000..9c0f5de
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Routing/NestedMatcherTest.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\system\Tests\Routing\NestedMatcherTest.
+ */
+
+namespace Drupal\system\Tests\Routing;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+
+use Drupal\simpletest\UnitTestBase;
+use Drupal\Core\Routing\HttpMethodMatcher;
+use Drupal\Core\Routing\NestedMatcher;
+use Drupal\Core\Routing\FirstEntryFinalMatcher;
+
+use Exception;
+
+/**
+ * Basic tests for the NestedMatcher class.
+ */
+class NestedMatcherTest extends UnitTestBase {
+
+  /**
+   * A collection of shared fixture data for tests.
+   *
+   * @var RoutingFixtures
+   */
+  protected $fixtures;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'NestedMatcher tests',
+      'description' => 'Confirm that the NestedMatcher system is working properly.',
+      'group' => 'Routing',
+    );
+  }
+
+  function __construct($test_id = NULL) {
+    parent::__construct($test_id);
+
+    $this->fixtures = new RoutingFixtures();
+  }
+  public function setUp() {
+    parent::setUp();
+  }
+
+  /**
+   * Confirms we can nest multiple partial matchers.
+   */
+  public function testNestedMatcher() {
+
+    $matcher = new NestedMatcher();
+
+    $matcher->setInitialMatcher(new MockPathMatcher($this->fixtures->sampleRouteCollection()));
+    $matcher->addPartialMatcher(new HttpMethodMatcher());
+    $matcher->setFinalMatcher(new FirstEntryFinalMatcher());
+
+    $request = Request::create('/path/one', 'GET');
+
+    $attributes = $matcher->matchRequest($request);
+
+    $this->assertEqual($attributes['_route'], 'route_a', 'The correct matching route was found.');
+  }
+}
+
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/PathMatcherTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/PathMatcherTest.php
new file mode 100644
index 0000000..10cfc82
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Routing/PathMatcherTest.php
@@ -0,0 +1,304 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\system\Tests\Routing\PartialMatcherTest.
+ */
+
+namespace Drupal\system\Tests\Routing;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+
+use Drupal\simpletest\UnitTestBase;
+use Drupal\Core\Routing\PathMatcher;
+use Drupal\Core\Database\Database;
+use Drupal\Core\Routing\MatcherDumper;
+
+use Exception;
+
+/**
+ * Basic tests for the UrlMatcherDumper.
+ */
+class PathMatcherTest extends UnitTestBase {
+
+  /**
+   * A collection of shared fixture data for tests.
+   *
+   * @var RoutingFixtures
+   */
+  protected $fixtures;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Path matcher tests',
+      'description' => 'Confirm that the path matching library is working correctly.',
+      'group' => 'Routing',
+    );
+  }
+
+  function __construct($test_id = NULL) {
+    parent::__construct($test_id);
+
+    $this->fixtures = new RoutingFixtures();
+  }
+
+  public function tearDown() {
+    $this->fixtures->dropTables(Database::getConnection());
+
+   parent::tearDown();
+  }
+
+  /**
+   * Confirms that the correct candidate outlines are generated.
+   */
+  public function testCandidateOutlines() {
+
+    $connection = Database::getConnection();
+    $matcher = new PathMatcher($connection);
+
+    $parts = array('node', '5', 'edit');
+
+    $candidates = $matcher->getCandidateOutlines($parts);
+
+    //debug($candidates);
+
+    $candidates = array_flip($candidates);
+
+    $this->assertTrue(count($candidates) == 4, 'Correct number of candidates found');
+    $this->assertTrue(array_key_exists('/node/5/edit', $candidates), 'First candidate found.');
+    $this->assertTrue(array_key_exists('/node/5/%', $candidates), 'Second candidate found.');
+    $this->assertTrue(array_key_exists('/node/%/edit', $candidates), 'Third candidate found.');
+    $this->assertTrue(array_key_exists('/node/%/%', $candidates), 'Fourth candidate found.');
+  }
+
+  /**
+   * Confirms that we can find routes with the exact incoming path.
+   */
+  function testExactPathMatch() {
+    $connection = Database::getConnection();
+    $matcher = new PathMatcher($connection, 'test_routes');
+
+    $this->fixtures->createTables($connection);
+
+    $dumper = new MatcherDumper($connection, 'test_routes');
+    $dumper->addRoutes($this->fixtures->sampleRouteCollection());
+    $dumper->dump();
+
+    $path = '/path/one';
+
+    $request = Request::create($path, 'GET');
+
+    $routes = $matcher->matchRequestPartial($request);
+
+    foreach ($routes as $route) {
+      $this->assertEqual($route->getPattern(), $path, 'Found path has correct pattern');
+    }
+  }
+
+  /**
+   * Confirms that we can find routes whose pattern would match the request.
+   */
+  function testOutlinePathMatch() {
+    $connection = Database::getConnection();
+    $matcher = new PathMatcher($connection, 'test_routes');
+
+    $this->fixtures->createTables($connection);
+
+    $dumper = new MatcherDumper($connection, 'test_routes');
+    $dumper->addRoutes($this->fixtures->complexRouteCollection());
+    $dumper->dump();
+
+    $path = '/path/1/one';
+
+    $request = Request::create($path, 'GET');
+
+    $routes = $matcher->matchRequestPartial($request);
+
+    // All of the matching paths have the correct pattern.
+    foreach ($routes as $route) {
+      $this->assertEqual($route->compile()->getPatternOutline(), '/path/%/one', 'Found path has correct pattern');
+    }
+
+    $this->assertEqual(count($routes->all()), 2, 'The correct number of routes was found.');
+    $this->assertNotNull($routes->get('route_a'), 'The first matching route was found.');
+    $this->assertNotNull($routes->get('route_b'), 'The second matching route was not found.');
+  }
+
+  /**
+   * Confirms that a trailing slash on the request doesn't result in a 404.
+   */
+  function testOutlinePathMatchTrailingSlash() {
+    $connection = Database::getConnection();
+    $matcher = new PathMatcher($connection, 'test_routes');
+
+    $this->fixtures->createTables($connection);
+
+    $dumper = new MatcherDumper($connection, 'test_routes');
+    $dumper->addRoutes($this->fixtures->complexRouteCollection());
+    $dumper->dump();
+
+    $path = '/path/1/one/';
+
+    $request = Request::create($path, 'GET');
+
+    $routes = $matcher->matchRequestPartial($request);
+
+    // All of the matching paths have the correct pattern.
+    foreach ($routes as $route) {
+      $this->assertEqual($route->compile()->getPatternOutline(), '/path/%/one', 'Found path has correct pattern');
+    }
+
+    $this->assertEqual(count($routes->all()), 2, 'The correct number of routes was found.');
+    $this->assertNotNull($routes->get('route_a'), 'The first matching route was found.');
+    $this->assertNotNull($routes->get('route_b'), 'The second matching route was not found.');
+  }
+
+  /**
+   * Confirms that we can find routes whose pattern would match the request.
+   */
+  function testOutlinePathMatchDefaults() {
+    $connection = Database::getConnection();
+    $matcher = new PathMatcher($connection, 'test_routes');
+
+    $this->fixtures->createTables($connection);
+
+    $collection = new RouteCollection();
+    $collection->add('poink', new Route('/some/path/{value}', array(
+      'value' => 'poink',
+    )));
+
+    $dumper = new MatcherDumper($connection, 'test_routes');
+    $dumper->addRoutes($collection);
+    $dumper->dump();
+
+    $path = '/some/path';
+
+    $request = Request::create($path, 'GET');
+
+    try {
+      $routes = $matcher->matchRequestPartial($request);
+
+      // All of the matching paths have the correct pattern.
+      foreach ($routes as $route) {
+        $compiled = $route->compile();
+        $this->assertEqual($route->compile()->getPatternOutline(), '/some/path', 'Found path has correct pattern');
+      }
+
+      $this->assertEqual(count($routes->all()), 1, 'The correct number of routes was found.');
+      $this->assertNotNull($routes->get('poink'), 'The first matching route was found.');
+    }
+    catch (ResourceNotFoundException $e) {
+      $this->fail('No matching route found with default argument value.');
+    }
+  }
+
+  /**
+   * Confirms that we can find routes whose pattern would match the request.
+   */
+  function testOutlinePathMatchDefaultsCollision() {
+    $connection = Database::getConnection();
+    $matcher = new PathMatcher($connection, 'test_routes');
+
+    $this->fixtures->createTables($connection);
+
+    $collection = new RouteCollection();
+    $collection->add('poink', new Route('/some/path/{value}', array(
+      'value' => 'poink',
+    )));
+    $collection->add('narf', new Route('/some/path/here'));
+
+    $dumper = new MatcherDumper($connection, 'test_routes');
+    $dumper->addRoutes($collection);
+    $dumper->dump();
+
+    $path = '/some/path';
+
+    $request = Request::create($path, 'GET');
+
+    try {
+      $routes = $matcher->matchRequestPartial($request);
+
+      // All of the matching paths have the correct pattern.
+      foreach ($routes as $route) {
+        $compiled = $route->compile();
+        $this->assertEqual($route->compile()->getPatternOutline(), '/some/path', 'Found path has correct pattern');
+      }
+
+      $this->assertEqual(count($routes->all()), 1, 'The correct number of routes was found.');
+      $this->assertNotNull($routes->get('poink'), 'The first matching route was found.');
+    }
+    catch (ResourceNotFoundException $e) {
+      $this->fail('No matching route found with default argument value.');
+    }
+  }
+
+  /**
+   * Confirms that we can find routes whose pattern would match the request.
+   */
+  function testOutlinePathMatchDefaultsCollision2() {
+    $connection = Database::getConnection();
+    $matcher = new PathMatcher($connection, 'test_routes');
+
+    $this->fixtures->createTables($connection);
+
+    $collection = new RouteCollection();
+    $collection->add('poink', new Route('/some/path/{value}', array(
+      'value' => 'poink',
+    )));
+    $collection->add('narf', new Route('/some/path/here'));
+
+    $dumper = new MatcherDumper($connection, 'test_routes');
+    $dumper->addRoutes($collection);
+    $dumper->dump();
+
+    $path = '/some/path/here';
+
+    $request = Request::create($path, 'GET');
+
+    try {
+      $routes = $matcher->matchRequestPartial($request);
+
+      // All of the matching paths have the correct pattern.
+      foreach ($routes as $route) {
+        $this->assertEqual($route->compile()->getPatternOutline(), '/some/path/here', 'Found path has correct pattern');
+      }
+
+      $this->assertEqual(count($routes->all()), 1, 'The correct number of routes was found.');
+      $this->assertNotNull($routes->get('narf'), 'The first matching route was found.');
+    }
+    catch (ResourceNotFoundException $e) {
+      $this->fail('No matching route found with default argument value.');
+    }
+  }
+
+  /**
+   * Confirm that an exception is thrown when no matching path is found.
+   */
+  function testOutlinePathNoMatch() {
+    $connection = Database::getConnection();
+    $matcher = new PathMatcher($connection, 'test_routes');
+
+    $this->fixtures->createTables($connection);
+
+    $dumper = new MatcherDumper($connection, 'test_routes');
+    $dumper->addRoutes($this->fixtures->complexRouteCollection());
+    $dumper->dump();
+
+    $path = '/no/such/path';
+
+    $request = Request::create($path, 'GET');
+
+    try {
+      $routes = $matcher->matchRequestPartial($request);
+      $this->fail(t('No exception was thrown.'));
+    }
+    catch (Exception $e) {
+      $this->assertTrue($e instanceof ResourceNotFoundException, 'The correct exception was thrown.');
+    }
+
+  }
+
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/RouteTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/RouteTest.php
new file mode 100644
index 0000000..9e4ba63
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Routing/RouteTest.php
@@ -0,0 +1,61 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\system\Tests\Routing\RouteTest.
+ */
+
+namespace Drupal\system\Tests\Routing;
+
+use Symfony\Component\Routing\Route;
+
+use Drupal\simpletest\UnitTestBase;
+use Drupal\Core\Database\Database;
+
+/**
+ * Basic tests for the Route.
+ */
+class RouteTest extends UnitTestBase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Routes',
+      'description' => 'Confirm that route object is functioning properly.',
+      'group' => 'Routing',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+  }
+
+  /**
+   * Confirms that a route compiles properly with the necessary data.
+   */
+  public function testCompilation() {
+    $route = new Route('/test/{something}/more');
+    $route->setOption('compiler_class', 'Drupal\Core\Routing\RouteCompiler');
+    $compiled = $route->compile();
+
+    $this->assertEqual($route, $compiled->getRoute(), 'Compiled route has the correct route object.');
+    $this->assertEqual($compiled->getFit(), 5 /* That's 101 binary*/, 'The fit was correct.');
+    $this->assertEqual($compiled->getPatternOutline(), '/test/%/more', 'The pattern outline was correct.');
+  }
+
+  /**
+   * Confirms that a compiled route with default values has the correct outline.
+   */
+  public function testCompilationDefaultValue() {
+    // Because "here" has a default value, it should not factor into the
+    // outline or the fitness.
+    $route = new Route('/test/{something}/more/{here}', array(
+      'here' => 'there',
+    ));
+    $route->setOption('compiler_class', 'Drupal\Core\Routing\RouteCompiler');
+    $compiled = $route->compile();
+
+    $this->assertEqual($route, $compiled->getRoute(), 'Compiled route has the correct route object.');
+    $this->assertEqual($compiled->getFit(), 5 /* That's 101 binary*/, 'The fit was correct.');
+    $this->assertEqual($compiled->getPatternOutline(), '/test/%/more', 'The pattern outline was correct.');
+  }
+
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/RouterTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/RouterTest.php
new file mode 100644
index 0000000..f5b4a18
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Routing/RouterTest.php
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\system\Tests\Routing\RouterTest.
+ */
+
+namespace Drupal\system\Tests\Routing;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Functional class for the full integrated routing system.
+ */
+class RouterTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('block', 'router_test');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Integrated Router tests',
+      'description' => 'Function Tests for the fully integrated routing system.',
+      'group' => 'Routing',
+    );
+  }
+
+  /**
+   * Confirms that the router can get to a controller.
+   */
+  public function testCanRoute() {
+    $this->drupalGet('router_test/test1');
+    $this->assertRaw('test1', 'The correct string was returned because the route was successful.');
+  }
+
+  /**
+   * Confirms that our default controller logic works properly.
+   */
+  public function testDefaultController() {
+    $this->drupalGet('router_test/test2');
+    $this->assertRaw('test2', 'The correct string was returned because the route was successful.');
+    $this->assertRaw('</html>', 'Page markup was found.');
+  }
+
+  /**
+   * Confirms that placeholders in paths work correctly.
+   */
+  public function testControllerPlaceholders() {
+    $value = $this->randomName();
+    $this->drupalGet('router_test/test3/' . $value);
+    $this->assertRaw($value, 'The correct string was returned because the route was successful.');
+    $this->assertRaw('</html>', 'Page markup was found.');
+  }
+
+  /**
+   * Confirms that default placeholders in paths work correctly.
+   */
+  public function testControllerPlaceholdersDefaultValues() {
+    $this->drupalGet('router_test/test4');
+    $this->assertRaw('narf', 'The correct string was returned because the route was successful.');
+    $this->assertRaw('</html>', 'Page markup was found.');
+  }
+
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/RoutingFixtures.php b/core/modules/system/lib/Drupal/system/Tests/Routing/RoutingFixtures.php
new file mode 100644
index 0000000..8937c75
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Routing/RoutingFixtures.php
@@ -0,0 +1,167 @@
+<?php
+
+namespace Drupal\system\Tests\Routing;
+
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+use Drupal\Core\Database\Connection;
+
+/**
+ * Utility methods to generate sample data, database configuration, etc.
+ */
+class RoutingFixtures {
+
+  public function createTables(Connection $connection) {
+    $tables = $this->routingTableDefinition();
+    $schema = $connection->schema();
+
+    foreach ($tables as $name => $table) {
+      $schema->dropTable($name);
+      $schema->createTable($name, $table);
+    }
+  }
+
+  public function dropTables(Connection $connection) {
+    $tables = $this->routingTableDefinition();
+    $schema = $connection->schema();
+
+    foreach ($tables as $name => $table) {
+      $schema->dropTable($name);
+    }
+  }
+
+  /**
+   * Returns a standard set of routes for testing.
+   *
+   * @return \Symfony\Component\Routing\RouteCollection
+   */
+  public function sampleRouteCollection() {
+    $collection = new RouteCollection();
+
+    $route = new Route('path/one');
+    $route->setRequirement('_method', 'GET');
+    $collection->add('route_a', $route);
+
+    $route = new Route('path/one');
+    $route->setRequirement('_method', 'PUT');
+    $collection->add('route_b', $route);
+
+    $route = new Route('path/two');
+    $route->setRequirement('_method', 'GET');
+    $collection->add('route_c', $route);
+
+    $route = new Route('path/three');
+    $collection->add('route_d', $route);
+
+    $route = new Route('path/two');
+    $route->setRequirement('_method', 'GET|HEAD');
+    $collection->add('route_e', $route);
+
+    return $collection;
+  }
+
+  /**
+   * Returns a complex set of routes for testing.
+   *
+   * @return \Symfony\Component\Routing\RouteCollection
+   */
+  public function complexRouteCollection() {
+    $collection = new RouteCollection();
+
+    $route = new Route('/path/{thing}/one');
+    $route->setRequirement('_method', 'GET');
+    $collection->add('route_a', $route);
+
+    $route = new Route('/path/{thing}/one');
+    $route->setRequirement('_method', 'PUT');
+    $collection->add('route_b', $route);
+
+    $route = new Route('/somewhere/{item}/over/the/rainbow');
+    $route->setRequirement('_method', 'GET');
+    $collection->add('route_c', $route);
+
+    $route = new Route('/another/{thing}/about/{item}');
+    $collection->add('route_d', $route);
+
+    $route = new Route('/path/add/one');
+    $route->setRequirement('_method', 'GET|HEAD');
+    $collection->add('route_e', $route);
+
+    return $collection;
+  }
+
+  public function routingTableDefinition() {
+
+    $tables['test_routes'] = array(
+      'description' => 'Maps paths to various callbacks (access, page and title)',
+      'fields' => array(
+        'name' => array(
+          'description' => 'Primary Key: Machine name of this route',
+          'type' => 'varchar',
+          'length' => 255,
+          'not null' => TRUE,
+          'default' => '',
+        ),
+        'pattern' => array(
+          'description' => 'The path pattern for this URI',
+          'type' => 'varchar',
+          'length' => 255,
+          'not null' => TRUE,
+          'default' => '',
+        ),
+        'pattern_outline' => array(
+          'description' => 'The pattern',
+          'type' => 'varchar',
+          'length' => 255,
+          'not null' => TRUE,
+          'default' => '',
+        ),
+        'route_set' => array(
+          'description' => 'The route set grouping to which a route belongs.',
+          'type' => 'varchar',
+          'length' => 255,
+          'not null' => TRUE,
+          'default' => '',
+        ),
+        'access_callback' => array(
+          'description' => 'The callback which determines the access to this router path. Defaults to user_access.',
+          'type' => 'varchar',
+          'length' => 255,
+          'not null' => TRUE,
+          'default' => '',
+        ),
+        'access_arguments' => array(
+          'description' => 'A serialized array of arguments for the access callback.',
+          'type' => 'blob',
+          'not null' => FALSE,
+        ),
+        'fit' => array(
+          'description' => 'A numeric representation of how specific the path is.',
+          'type' => 'int',
+          'not null' => TRUE,
+          'default' => 0,
+        ),
+        'number_parts' => array(
+          'description' => 'Number of parts in this router path.',
+          'type' => 'int',
+          'not null' => TRUE,
+          'default' => 0,
+          'size' => 'small',
+        ),
+        'route' => array(
+          'description' => 'A serialized Route object',
+          'type' => 'text',
+        ),
+      ),
+      'indexes' => array(
+        'fit' => array('fit'),
+        'pattern_outline' => array('pattern_outline'),
+        'route_set' => array('route_set'),
+      ),
+      'primary key' => array('name'),
+    );
+
+    return $tables;
+  }
+}
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index a6856ec..3a16394 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -1261,6 +1261,63 @@ function system_schema() {
     ),
   );
 
+  $schema['router'] = array(
+    'description' => 'Maps paths to various callbacks (access, page and title)',
+    'fields' => array(
+      'name' => array(
+        'description' => 'Primary Key: Machine name of this route',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'pattern' => array(
+        'description' => 'The path pattern for this URI',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'pattern_outline' => array(
+        'description' => 'The pattern',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'route_set' => array(
+        'description' => 'The route set grouping to which a route belongs.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'fit' => array(
+        'description' => 'A numeric representation of how specific the path is.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'route' => array(
+        'description' => 'A serialized Route object',
+        'type' => 'text',
+      ),
+      'number_parts' => array(
+        'description' => 'Number of parts in this router path.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'size' => 'small',
+      ),
+    ),
+    'indexes' => array(
+      'fit' => array('fit'),
+      'pattern_outline' => array('pattern_outline'),
+      'route_set' => array('route_set'),
+    ),
+    'primary key' => array('name'),
+  );
+
   $schema['semaphore'] = array(
     'description' => 'Table for holding semaphores, locks, flags, etc. that cannot be stored as Drupal variables since they must not be cached.',
     'fields' => array(
@@ -1950,6 +2007,75 @@ function system_update_8019() {
   db_drop_table('registry_file');
 }
 
+/*
+ * Create the new routing table.
+ */
+function system_update_8020() {
+
+  $tables['router'] = array(
+    'description' => 'Maps paths to various callbacks (access, page and title)',
+    'fields' => array(
+      'name' => array(
+        'description' => 'Primary Key: Machine name of this route',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'pattern' => array(
+        'description' => 'The path pattern for this URI',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'pattern_outline' => array(
+        'description' => 'The pattern',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'route_set' => array(
+        'description' => 'The route set grouping to which a route belongs.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'fit' => array(
+        'description' => 'A numeric representation of how specific the path is.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'route' => array(
+        'description' => 'A serialized Route object',
+        'type' => 'text',
+      ),
+      'number_parts' => array(
+        'description' => 'Number of parts in this router path.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'size' => 'small',
+      ),
+    ),
+    'indexes' => array(
+      'fit' => array('fit'),
+      'pattern_outline' => array('pattern_outline'),
+      'route_set' => array('route_set'),
+    ),
+    'primary key' => array('name'),
+  );
+
+  $schema = Database::getConnection()->schema();
+
+  $schema->dropTable('router');
+
+  $schema->createTable('router', $tables['router']);
+}
+
 /**
  * @} End of "defgroup updates-7.x-to-8.x".
  * The next series of updates should start at 9000.
diff --git a/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/TestControllers.php b/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/TestControllers.php
new file mode 100644
index 0000000..fa92fd8
--- /dev/null
+++ b/core/modules/system/tests/modules/router_test/lib/Drupal/router_test/TestControllers.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\router_test\TestControllers.
+ */
+
+namespace Drupal\router_test;
+
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * Controller routines for testing the routing system.
+ */
+class TestControllers {
+
+  public function test1() {
+    return new Response('test1');
+  }
+
+  public function test2() {
+    return "test2";
+  }
+
+  public function test3($value) {
+    return $value;
+  }
+
+  public function test4($value) {
+    return $value;
+  }
+
+}
diff --git a/core/modules/system/tests/modules/router_test/router_test.info b/core/modules/system/tests/modules/router_test/router_test.info
new file mode 100644
index 0000000..d729865
--- /dev/null
+++ b/core/modules/system/tests/modules/router_test/router_test.info
@@ -0,0 +1,6 @@
+name = "Router test"
+description = "Support module for routing testing."
+package = Testing
+version = VERSION
+core = 8.x
+hidden = TRUE
diff --git a/core/modules/system/tests/modules/router_test/router_test.module b/core/modules/system/tests/modules/router_test/router_test.module
new file mode 100644
index 0000000..4da939d
--- /dev/null
+++ b/core/modules/system/tests/modules/router_test/router_test.module
@@ -0,0 +1,34 @@
+<?php
+
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * Implements hook_router_info().
+ */
+function router_test_route_info() {
+  $collection = new RouteCollection();
+
+  $route = new Route('router_test/test1', array(
+    '_controller' => '\Drupal\router_test\TestControllers::test1'
+  ));
+  $collection->add('router_test_1', $route);
+
+  $route = new Route('router_test/test2', array(
+    '_content' => '\Drupal\router_test\TestControllers::test2'
+  ));
+  $collection->add('router_test_2', $route);
+
+  $route = new Route('router_test/test3/{value}', array(
+    '_content' => '\Drupal\router_test\TestControllers::test3'
+  ));
+  $collection->add('router_test_3', $route);
+
+  $route = new Route('router_test/test4/{value}', array(
+    '_content' => '\Drupal\router_test\TestControllers::test4',
+    'value' => 'narf',
+  ));
+  $collection->add('router_test_4', $route);
+
+  return $collection;
+}
diff --git a/core/modules/user/user.pages.inc b/core/modules/user/user.pages.inc
index 9dd2cc9..34f24ab 100644
--- a/core/modules/user/user.pages.inc
+++ b/core/modules/user/user.pages.inc
@@ -420,9 +420,7 @@ function user_page() {
   global $user;
   if ($user->uid) {
     // @todo: Cleaner sub request handling.
-    $request = drupal_container()->get('request');
-    $subrequest = Request::create('/user/' . $user->uid, 'GET', $request->query->all(), $request->cookies->all(), array(), $request->server->all());
-    return drupal_container()->get('http_kernel')->handle($subrequest, HttpKernelInterface::SUB_REQUEST);
+    drupal_goto('user/' . $user->uid);
   }
   else {
     return drupal_get_form('user_login');
diff --git a/core/vendor/Routing b/core/vendor/Routing
new file mode 160000
index 0000000..a05bcaa
--- /dev/null
+++ b/core/vendor/Routing
@@ -0,0 +1 @@
+Subproject commit a05bcaaaa43025037a0667e158aed9b65a147e80
