diff --git a/core/lib/Drupal/Core/ContentNegotiation.php b/core/lib/Drupal/Core/ContentNegotiation.php
new file mode 100644
index 0000000..6302db8
--- /dev/null
+++ b/core/lib/Drupal/Core/ContentNegotiation.php
@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\ContentNegotiation.
+ */
+
+namespace Drupal\Core;
+
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * This class is a central library for content type negotiation.
+ *
+ * @todo Replace this class with a real content negotiation library based on
+ *   mod_negotiation. Development of that is a work in progress.
+ */
+class ContentNegotiation {
+
+  /**
+   * Gets the normalized type of a request.
+   *
+   * The normalized type is a short, lowercase version of the format, such as
+   * 'html', 'json' or 'atom'.
+   *
+   * @param Symfony\Component\HttpFoundation\Request $request
+   *   The request object from which to extract the content type.
+   *
+   * @return
+   *   The normalized type of a given request.
+   */
+  public function getContentType(Request $request) {
+    // AJAX iframe uploads need special handling, because they contain a JSON
+    // response wrapped in <textarea>.
+    if ($request->get('ajax_iframe_upload', FALSE)) {
+      return 'iframeupload';
+    }
+
+    // AJAX calls need to be run through ajax rendering functions
+    elseif ($request->isXmlHttpRequest()) {
+      return 'ajax';
+    }
+
+    foreach ($request->getAcceptableContentTypes() as $mime_type) {
+      $format = $request->getFormat($mime_type);
+      if (!is_null($format)) {
+        return $format;
+      }
+    }
+
+    // Do HTML last so that it always wins.
+    return 'html';
+  }
+}
diff --git a/core/lib/Drupal/Core/Database/DatabaseExceptionWrapper.php b/core/lib/Drupal/Core/Database/DatabaseExceptionWrapper.php
new file mode 100644
index 0000000..b212478
--- /dev/null
+++ b/core/lib/Drupal/Core/Database/DatabaseExceptionWrapper.php
@@ -0,0 +1,19 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Database\DatabaseExceptionWrapper.
+ */
+
+namespace Drupal\Core\Database;
+
+use RuntimeException;
+
+/**
+ * This wrapper class serves only to provide additional debug information.
+ *
+ * This class will always wrap a PDOException.
+ */
+class DatabaseExceptionWrapper extends RuntimeException implements DatabaseException {
+
+}
diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php
new file mode 100644
index 0000000..dc6762b
--- /dev/null
+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -0,0 +1,90 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\DrupalKernel.
+ */
+
+namespace Drupal\Core;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\HttpKernel\HttpKernel;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
+use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
+use Symfony\Component\HttpKernel\EventListener\ExceptionListener;
+use Drupal\Core\EventSubscriber\ViewSubscriber;
+use Drupal\Core\EventSubscriber\AccessSubscriber;
+use Drupal\Core\EventSubscriber\FinishResponseSubscriber;
+use Drupal\Core\EventSubscriber\PathSubscriber;
+use Drupal\Core\EventSubscriber\LegacyRequestSubscriber;
+use Drupal\Core\EventSubscriber\LegacyControllerSubscriber;
+use Drupal\Core\EventSubscriber\MaintenanceModeSubscriber;
+use Drupal\Core\EventSubscriber\RequestCloseSubscriber;
+use Drupal\Core\EventSubscriber\RouterListener;
+
+use Exception;
+
+/**
+ * The DrupalKernel class is the core of Drupal itself.
+ */
+class DrupalKernel extends HttpKernel {
+
+    /**
+     * The event dispatcher used by this kernel.
+     *
+     * @var Symfony\Component\EventDispatcher\EventDispatcherInterface
+     */
+    protected $dispatcher;
+
+    /**
+     * The controller resolver that will extract the controller from a Request.
+     *
+     * @var Symfony\Component\HttpKernel\Controller\ControllerResolverInterface
+     */
+    protected $resolver;
+
+
+    /**
+     * Constructor.
+     *
+     * @param Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
+     *   An EventDispatcherInterface instance.
+     * @param Symfony\Component\HttpKernel\Controller\ControllerResolverInterface $resolver
+     *   A ControllerResolverInterface instance.
+     */
+   public function __construct(EventDispatcherInterface $dispatcher, ControllerResolverInterface $resolver) {
+      parent::__construct($dispatcher, $resolver);
+      $this->dispatcher = $dispatcher;
+      $this->resolver = $resolver;
+
+      $this->matcher = new UrlMatcher();
+      $this->dispatcher->addSubscriber(new RouterListener($this->matcher));
+
+      $negotiation = new ContentNegotiation();
+
+      // @todo Make this extensible rather than just hard coding some.
+      // @todo Add a subscriber to handle other things, too, like our Ajax
+      //   replacement system.
+      $this->dispatcher->addSubscriber(new ViewSubscriber($negotiation));
+      $this->dispatcher->addSubscriber(new AccessSubscriber());
+      $this->dispatcher->addSubscriber(new MaintenanceModeSubscriber());
+      $this->dispatcher->addSubscriber(new PathSubscriber());
+      $this->dispatcher->addSubscriber(new LegacyRequestSubscriber());
+      $this->dispatcher->addSubscriber(new LegacyControllerSubscriber());
+      $this->dispatcher->addSubscriber(new FinishResponseSubscriber());
+      $this->dispatcher->addSubscriber(new RequestCloseSubscriber());
+
+      // Some other form of error occured that wasn't handled by another kernel
+      // listener. That could mean that it's a method/mime-type/error
+      // combination that is not accounted for, or some other type of error.
+      // Either way, treat it as a server-level error and return an HTTP 500.
+      // By default, this will be an HTML-type response because that's a decent
+      // best guess if we don't know otherwise.
+      $this->dispatcher->addSubscriber(new ExceptionListener(array(new ExceptionController($this, $negotiation), 'execute')));
+    }
+}
diff --git a/core/lib/Drupal/Core/EventSubscriber/AccessSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/AccessSubscriber.php
new file mode 100644
index 0000000..4d10489
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/AccessSubscriber.php
@@ -0,0 +1,53 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\EventSubscriber\AccessSubscriber.
+ */
+
+namespace Drupal\Core\EventSubscriber;
+
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Access subscriber for controller requests.
+ */
+class AccessSubscriber implements EventSubscriberInterface {
+
+  /**
+   * Verifies that the current user can access the requested path.
+   *
+   * @todo This is a total hack to keep our current access system working. It
+   *   should be replaced with something robust and injected at some point.
+   *
+   * @param Symfony\Component\HttpKernel\Event\GetResponseEvent $event
+   *   The Event to process.
+   */
+  public function onKernelRequestAccessCheck(GetResponseEvent $event) {
+
+    $router_item = $event->getRequest()->attributes->get('drupal_menu_item');
+
+    if (isset($router_item['access']) && !$router_item['access']) {
+      throw new AccessDeniedHttpException();
+    }
+  }
+
+  /**
+   * Registers the methods in this class that should be listeners.
+   *
+   * @return array
+   *   An array of event listener definitions.
+   */
+  static function getSubscribedEvents() {
+    $events[KernelEvents::REQUEST][] = array('onKernelRequestAccessCheck', 30);
+
+    return $events;
+  }
+}
diff --git a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php
new file mode 100644
index 0000000..6e23845
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php
@@ -0,0 +1,83 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\EventSubscriber\FinishResponseSubscriber.
+ */
+
+namespace Drupal\Core\EventSubscriber;
+
+use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Response subscriber to handle finished responses.
+ */
+class FinishResponseSubscriber implements EventSubscriberInterface {
+
+  /**
+   * Sets extra headers on successful responses.
+   *
+   * @param Symfony\Component\HttpKernel\Event\FilterResponseEvent $event
+   *   The event to process.
+   */
+  public function onRespond(FilterResponseEvent $event) {
+    $response = $event->getResponse();
+
+    // Set the X-UA-Compatible HTTP header to force IE to use the most recent
+    // rendering engine or use Chrome's frame rendering engine if available.
+    $response->headers->set('X-UA-Compatible', 'IE=edge,chrome=1', false);
+
+    // Set the Content-language header.
+    $response->headers->set('Content-language', drupal_container()->get(LANGUAGE_TYPE_INTERFACE)->langcode);
+
+    // Because pages are highly dynamic, set the last-modified time to now
+    // since the page is in fact being regenerated right now.
+    // @todo Remove this and use a more intelligent default so that HTTP
+    // caching can function properly.
+    $response->headers->set('Last-Modified', gmdate(DATE_RFC1123, REQUEST_TIME));
+
+    // Also give each page a unique ETag. This will force clients to include
+    // both an If-Modified-Since header and an If-None-Match header when doing
+    // conditional requests for the page (required by RFC 2616, section 13.3.4),
+    // making the validation more robust. This is a workaround for a bug in
+    // Mozilla Firefox that is triggered when Drupal's caching is enabled and
+    // the user accesses Drupal via an HTTP proxy (see
+    // https://bugzilla.mozilla.org/show_bug.cgi?id=269303): When an
+    // authenticated user requests a page, and then logs out and requests the
+    // same page again, Firefox may send a conditional request based on the
+    // page that was cached locally when the user was logged in. If this page
+    // did not have an ETag header, the request only contains an
+    // If-Modified-Since header. The date will be recent, because with
+    // authenticated users the Last-Modified header always refers to the time
+    // of the request. If the user accesses Drupal via a proxy server, and the
+    // proxy already has a cached copy of the anonymous page with an older
+    // Last-Modified date, the proxy may respond with 304 Not Modified, making
+    // the client think that the anonymous and authenticated pageviews are
+    // identical.
+    // @todo Remove this line as no longer necessary per
+    //   http://drupal.org/node/1573064
+    $response->headers->set('ETag', '"' . REQUEST_TIME . '"');
+
+    // Authenticated users are always given a 'no-cache' header, and will fetch
+    // a fresh page on every request. This prevents authenticated users from
+    // seeing locally cached pages.
+    // @todo Revisit whether or not this is still appropriate now that the
+    //   Response object does its own cache control procesisng and we intend to
+    //   use partial page caching more extensively.
+    $response->headers->set('Expires', 'Sun, 19 Nov 1978 05:00:00 GMT');
+    $response->headers->set('Cache-Control', 'no-cache, must-revalidate, post-check=0, pre-check=0');
+  }
+
+  /**
+   * Registers the methods in this class that should be listeners.
+   *
+   * @return array
+   *   An array of event listener definitions.
+   */
+  static function getSubscribedEvents() {
+    $events[KernelEvents::RESPONSE][] = array('onRespond');
+    return $events;
+  }
+}
diff --git a/core/lib/Drupal/Core/EventSubscriber/LegacyControllerSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/LegacyControllerSubscriber.php
new file mode 100644
index 0000000..48cc8a0
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/LegacyControllerSubscriber.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\EventSubscriber\LegacyControllerSubscriber.
+ */
+
+namespace Drupal\Core\EventSubscriber;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Access subscriber for controller requests.
+ */
+class LegacyControllerSubscriber implements EventSubscriberInterface {
+
+  /**
+   * Wraps legacy controllers in a closure to handle old-style arguments.
+   *
+   * This is a backward compatibility layer only.  This is a rather ugly way
+   * to piggyback Drupal's existing menu router items onto the Symfony model,
+   * but it works for now.  If we did not do this, any menu router item with
+   * a variable number of arguments would fail to work.  This bypasses Symfony's
+   * controller argument handling entirely and lets the old-style approach work.
+   *
+   * @todo Convert Drupal to use the IETF-draft-RFC style {placeholders}. That
+   *   will allow us to use the native Symfony conversion, including
+   *   out-of-order argument mapping, name-based mapping, and with another
+   *   listener auto-conversion of parameters to full objects. That may
+   *   necessitate not using func_get_args()-based controllers. That is likely
+   *   for the best, as those are quite hard to document anyway.
+   *
+   * @param Symfony\Component\HttpKernel\Event\FilterControllerEvent $event
+   *   The Event to process.
+   */
+  public function onKernelControllerLegacy(FilterControllerEvent $event) {
+    $router_item = $event->getRequest()->attributes->get('drupal_menu_item');
+    $controller = $event->getController();
+
+    // This BC logic applies only to functions. Otherwise, skip it.
+    if (is_string($controller) && function_exists($controller)) {
+      $new_controller = function() use ($router_item) {
+        return call_user_func_array($router_item['page_callback'], $router_item['page_arguments']);
+      };
+      $event->setController($new_controller);
+    }
+  }
+
+  /**
+   * Registers the methods in this class that should be listeners.
+   *
+   * @return array
+   *   An array of event listener definitions.
+   */
+  static function getSubscribedEvents() {
+    $events[KernelEvents::CONTROLLER][] = array('onKernelControllerLegacy', 30);
+
+    return $events;
+  }
+}
diff --git a/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php
new file mode 100644
index 0000000..2620c07
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\EventSubscriber\LegacyRequestSubscriber.
+ */
+
+namespace Drupal\Core\EventSubscriber;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * KernelEvents::REQUEST event subscriber to initialize theme and modules.
+ *
+ * @todo Remove this subscriber when all of the code in it has been refactored.
+ */
+class LegacyRequestSubscriber implements EventSubscriberInterface {
+
+  /**
+   * Initializes the rest of the legacy Drupal subsystems.
+   *
+   * @param Symfony\Component\HttpKernel\Event\GetResponseEvent $event
+   *   The Event to process.
+   */
+  public function onKernelRequestLegacy(GetResponseEvent $event) {
+    if ($event->getRequestType() == HttpKernelInterface::MASTER_REQUEST) {
+      menu_set_custom_theme();
+      drupal_theme_initialize();
+      module_invoke_all('init');
+
+      // Tell Drupal it is now fully bootstrapped (for the benefit of code that
+      // calls drupal_get_bootstrap_phase()), but without having
+      // _drupal_bootstrap_full() do anything, since we've already done the
+      // equivalent above and in earlier listeners.
+      _drupal_bootstrap_full(TRUE);
+      drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
+    }
+  }
+
+  /**
+   * Registers the methods in this class that should be listeners.
+   *
+   * @return array
+   *   An array of event listener definitions.
+   */
+  static function getSubscribedEvents() {
+    $events[KernelEvents::REQUEST][] = array('onKernelRequestLegacy', 90);
+
+    return $events;
+  }
+}
diff --git a/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php
new file mode 100644
index 0000000..daed1c9
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php
@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\EventSubscriber\MaintenanceModeSubscriber.
+ */
+
+namespace Drupal\Core\EventSubscriber;
+
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Maintenance mode subscriber for controller requests.
+ */
+class MaintenanceModeSubscriber implements EventSubscriberInterface {
+
+  /**
+   * Response with the maintenance page when the site is offline.
+   *
+   * @param Symfony\Component\HttpKernel\Event\GetResponseEvent $event
+   *   The Event to process.
+   */
+  public function onKernelRequestMaintenanceModeCheck(GetResponseEvent $event) {
+    // Check if the site is offline.
+    $status = _menu_site_is_offline() ? MENU_SITE_OFFLINE : MENU_SITE_ONLINE;
+
+    // Allow other modules to change the site status but not the path because
+    // that would not change the global variable. hook_url_inbound_alter() can
+    // be used to change the path. Code later will not use the $read_only_path
+    // variable.
+    $read_only_path = !empty($path) ? $path : $event->getRequest()->attributes->get('system_path');
+    drupal_alter('menu_site_status', $status, $read_only_path);
+
+    // Only continue if the site is online.
+    if ($status != MENU_SITE_ONLINE) {
+      // Deliver the 503 page.
+      drupal_maintenance_theme();
+      drupal_set_title(t('Site under maintenance'));
+      $content = theme('maintenance_page', array('content' => filter_xss_admin(variable_get('maintenance_mode_message', t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('@site' => variable_get('site_name', 'Drupal')))))));
+      $response = new Response('Service unavailable', 503);
+      $response->setContent($content);
+      $event->setResponse($response);
+    }
+  }
+
+  /**
+   * Registers the methods in this class that should be listeners.
+   *
+   * @return array
+   *   An array of event listener definitions.
+   */
+  static function getSubscribedEvents() {
+    $events[KernelEvents::REQUEST][] = array('onKernelRequestMaintenanceModeCheck', 40);
+    return $events;
+  }
+}
diff --git a/core/lib/Drupal/Core/EventSubscriber/PathListenerBase.php b/core/lib/Drupal/Core/EventSubscriber/PathListenerBase.php
new file mode 100644
index 0000000..fd0a765
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/PathListenerBase.php
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\EventSubscriber\PathListenerBase.
+ */
+
+namespace Drupal\Core\EventSubscriber;
+
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Base class for listeners that are manipulating the path.
+ */
+abstract class PathListenerBase {
+
+  public function extractPath(Request $request) {
+    $path = $request->attributes->get('system_path');
+    return isset($path) ? $path : trim($request->getPathInfo(), '/');
+  }
+
+  public function setPath(Request $request, $path) {
+    $request->attributes->set('system_path', $path);
+
+    // @todo Remove this line once code has been refactored to use the request
+    //   object directly.
+    _current_path($path);
+  }
+}
diff --git a/core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php
new file mode 100644
index 0000000..1e2a95a
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php
@@ -0,0 +1,127 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\EventSubscriber\PathSubscriber.
+ */
+
+namespace Drupal\Core\EventSubscriber;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Access subscriber for controller requests.
+ */
+class PathSubscriber extends PathListenerBase implements EventSubscriberInterface {
+
+  /**
+   * Resolve the system path.
+   *
+   * @todo The path system should be objectified to remove the function calls in
+   *   this method.
+   *
+   * @param Symfony\Component\HttpKernel\Event\GetResponseEvent $event
+   *   The Event to process.
+   */
+  public function onKernelRequestPathResolve(GetResponseEvent $event) {
+    $request = $event->getRequest();
+
+    $path = $this->extractPath($request);
+
+    $path = drupal_get_normal_path($path);
+
+    $this->setPath($request, $path);
+  }
+
+  /**
+   * Resolve the front-page default path.
+   *
+   * @todo The path system should be objectified to remove the function calls in
+   *   this method.
+   *
+   * @param Symfony\Component\HttpKernel\Event\GetResponseEvent $event
+   *   The Event to process.
+   */
+  public function onKernelRequestFrontPageResolve(GetResponseEvent $event) {
+    $request = $event->getRequest();
+    $path = $this->extractPath($request);
+
+    if (empty($path)) {
+      // @todo Temporary hack. Fix when configuration is injectable.
+      $path = variable_get('site_frontpage', 'user');
+    }
+
+    $this->setPath($request, $path);
+  }
+
+  /**
+   * Decode language information embedded in the request path.
+   *
+   * @todo Refactor this entire method to inline the relevant portions of
+   *   drupal_language_initialize(). See the inline comment for more details.
+   *
+   * @param Symfony\Component\HttpKernel\Event\GetResponseEvent $event
+   *   The Event to process.
+   */
+  public function onKernelRequestLanguageResolve(GetResponseEvent $event) {
+    $request = $event->getRequest();
+    $path = $this->extractPath($request);
+
+    // drupal_language_initialize() combines:
+    // - Determination of language from $request information (e.g., path).
+    // - Determination of language from other information (e.g., site default).
+    // - Population of determined language into drupal_container().
+    // - Removal of language code from _current_path().
+    // @todo Decouple the above, but for now, invoke it and update the path
+    //   prior to front page and alias resolution. When above is decoupled, also
+    //   add 'langcode' (determined from $request only) to $request->attributes.
+    _current_path($path);
+    drupal_language_initialize();
+    $path = _current_path();
+
+    $this->setPath($request, $path);
+  }
+
+  /**
+   * Decodes the path of the request.
+   *
+   * Parameters in the URL sometimes represent code-meaningful strings. It is
+   * therefore useful to always urldecode() those values so that individual
+   * controllers need not concern themselves with it. This is Drupal-specific
+   * logic and may not be familiar for developers used to other Symfony-family
+   * projects.
+   *
+   * @todo Revisit whether or not this logic is appropriate for here or if
+   *   controllers should be required to implement this logic themselves. If we
+   *   decide to keep this code, remove this TODO.
+   *
+   * @param Symfony\Component\HttpKernel\Event\GetResponseEvent $event
+   *   The Event to process.
+   */
+  public function onKernelRequestDecodePath(GetResponseEvent $event) {
+    $request = $event->getRequest();
+    $path = $this->extractPath($request);
+
+    $path = urldecode($path);
+
+    $this->setPath($request, $path);
+  }
+
+  /**
+   * Registers the methods in this class that should be listeners.
+   *
+   * @return array
+   *   An array of event listener definitions.
+   */
+  static function getSubscribedEvents() {
+    $events[KernelEvents::REQUEST][] = array('onKernelRequestDecodePath', 200);
+    $events[KernelEvents::REQUEST][] = array('onKernelRequestLanguageResolve', 150);
+    $events[KernelEvents::REQUEST][] = array('onKernelRequestFrontPageResolve', 101);
+    $events[KernelEvents::REQUEST][] = array('onKernelRequestPathResolve', 100);
+
+    return $events;
+  }
+}
diff --git a/core/lib/Drupal/Core/EventSubscriber/RequestCloseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/RequestCloseSubscriber.php
new file mode 100644
index 0000000..9b3ef45
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/RequestCloseSubscriber.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\EventSubscriber\RequestCloseSubscriber.
+ */
+
+namespace Drupal\Core\EventSubscriber;
+
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\Event\PostResponseEvent;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Subscriber for all responses.
+ */
+class RequestCloseSubscriber implements EventSubscriberInterface {
+
+  /**
+   * Performs end of request tasks.
+   *
+   * @todo The body of this function has just been copied almost verbatim from
+   *   drupal_page_footer(). There's probably a lot in here that needs to get
+   *   removed/changed.
+   *
+   * @param Symfony\Component\HttpKernel\Event\PostResponseEvent $event
+   *   The Event to process.
+   */
+  public function onTerminate(PostResponseEvent $event) {
+    global $user;
+
+    module_invoke_all('exit');
+
+    // Commit the user session, if needed.
+    drupal_session_commit();
+    $response = $event->getResponse();
+    $config = config('system.performance');
+
+    if ($config->get('cache') && ($cache = drupal_page_set_cache())) {
+      drupal_serve_page_from_cache($cache);
+    }
+    else {
+      ob_flush();
+    }
+
+    _registry_check_code(REGISTRY_WRITE_LOOKUP_CACHE);
+    drupal_cache_system_paths();
+    module_implements_write_cache();
+    system_run_automated_cron();
+  }
+
+  /**
+   * Registers the methods in this class that should be listeners.
+   *
+   * @return array
+   *   An array of event listener definitions.
+   */
+  static function getSubscribedEvents() {
+    $events[KernelEvents::TERMINATE][] = array('onTerminate');
+
+    return $events;
+  }
+}
diff --git a/core/lib/Drupal/Core/EventSubscriber/RouterListener.php b/core/lib/Drupal/Core/EventSubscriber/RouterListener.php
new file mode 100644
index 0000000..5b6246d
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/RouterListener.php
@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\EventSubscriber\RouterListener.
+ */
+
+namespace Drupal\Core\EventSubscriber;
+
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+use Symfony\Component\HttpKernel\EventListener\RouterListener as SymfonyRouterListener;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
+use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\Exception\MethodNotFoundException;
+
+/**
+ * Drupal-specific Router listener.
+ *
+ * This is the bridge from the kernel to the UrlMatcher.
+ */
+class RouterListener extends SymfonyRouterListener {
+
+  protected $urlMatcher;
+  protected $logger;
+
+  public function __construct(UrlMatcherInterface $urlMatcher, LoggerInterface $logger = null) {
+    parent::__construct($urlMatcher, $logger);
+    $this->urlMatcher = $urlMatcher;
+    $this->logger = $logger;
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * This method is nearly identical to the parent, except it passes the
+   * $request->attributes->get('system_path') variable to the matcher.
+   * That is where Drupal stores its processed, de-aliased, and sanitized
+   * internal path. We also pass the full request object to the URL Matcher,
+   * since we want attributes to be available to the matcher and to controllers.
+   */
+  public function onKernelRequest(GetResponseEvent $event) {
+    $request = $event->getRequest();
+
+    if (HttpKernelInterface::MASTER_REQUEST === $event->getRequestType()) {
+      $this->urlMatcher->getContext()->fromRequest($request);
+      $this->urlMatcher->setRequest($request);
+    }
+
+    if ($request->attributes->has('_controller')) {
+      // Routing is already done.
+      return;
+    }
+
+    // Add attributes based on the path info (routing).
+    try {
+      $parameters = $this->urlMatcher->match($request->attributes->get('system_path'));
+
+      if (null !== $this->logger) {
+          $this->logger->info(sprintf('Matched route "%s" (parameters: %s)', $parameters['_route'], $this->parametersToString($parameters)));
+      }
+
+      $request->attributes->add($parameters);
+      unset($parameters['_route']);
+      unset($parameters['_controller']);
+      $request->attributes->set('_route_params', $parameters);
+    }
+    catch (ResourceNotFoundException $e) {
+      $message = sprintf('No route found for "%s %s"', $request->getMethod(), $request->getPathInfo());
+
+      throw new NotFoundHttpException($message, $e);
+    }
+    catch (MethodNotAllowedException $e) {
+      $message = sprintf('No route found for "%s %s": Method Not Allowed (Allow: %s)', $request->getMethod(), $request->getPathInfo(), strtoupper(implode(', ', $e->getAllowedMethods())));
+
+      throw new MethodNotAllowedHttpException($e->getAllowedMethods(), $message, $e);
+    }
+  }
+}
diff --git a/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php
new file mode 100644
index 0000000..4761d47
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php
@@ -0,0 +1,128 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\EventSubscriber\ViewSubscriber.
+ */
+
+namespace Drupal\Core\EventSubscriber;
+
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+use Drupal\Core\ContentNegotiation;
+
+/**
+ * Main subscriber for VIEW HTTP responses.
+ */
+class ViewSubscriber implements EventSubscriberInterface {
+
+  protected $negotiation;
+
+  public function __construct(ContentNegotiation $negotiation) {
+    $this->negotiation = $negotiation;
+  }
+
+  /**
+   * Processes a successful controller into an HTTP 200 response.
+   *
+   * Some controllers may not return a response object but simply the body of
+   * one.  The VIEW event is called in that case, to allow us to mutate that
+   * body into a Response object.  In particular we assume that the return
+   * from an JSON-type response is a JSON string, so just wrap it into a
+   * Response object.
+   *
+   * @param Symfony\Component\HttpKernel\Event\GetResponseEvent $event
+   *   The Event to process.
+   */
+  public function onView(GetResponseEvent $event) {
+
+    $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));
+    }
+  }
+
+  public function onJson(GetResponseEvent $event) {
+    $page_callback_result = $event->getControllerResult();
+
+    $response = new JsonResponse();
+    $response->setContent($page_callback_result);
+
+    return $response;
+  }
+
+  public function onAjax(GetResponseEvent $event) {
+    $page_callback_result = $event->getControllerResult();
+
+    // Construct the response content from the page callback result.
+    $commands = ajax_prepare_response($page_callback_result);
+    $json = ajax_render($commands);
+
+    // Build the actual response object.
+    $response = new JsonResponse();
+    $response->setContent($json);
+
+    return $response;
+  }
+
+  public function onIframeUpload(GetResponseEvent $event) {
+    $page_callback_result = $event->getControllerResult();
+
+    // Construct the response content from the page callback result.
+    $commands = ajax_prepare_response($page_callback_result);
+    $json = ajax_render($commands);
+
+    // Browser IFRAMEs expect HTML. Browser extensions, such as Linkification
+    // and Skype's Browser Highlighter, convert URLs, phone numbers, etc. into
+    // links. This corrupts the JSON response. Protect the integrity of the
+    // JSON data by making it the value of a textarea.
+    // @see http://malsup.com/jquery/form/#file-upload
+    // @see http://drupal.org/node/1009382
+    $html = '<textarea>' . $json . '</textarea>';
+
+    return new Response($html);
+  }
+
+  /**
+   * Processes a successful controller into an HTTP 200 response.
+   *
+   * Some controllers may not return a response object but simply the body of
+   * one. The VIEW event is called in that case, to allow us to mutate that
+   * body into a Response object. In particular we assume that the return from
+   * an HTML-type response is a render array from a legacy page callback and
+   * render it.
+   *
+   * @param Symfony\Component\HttpKernel\Event\GetResponseEvent $event
+   *   The Event to process.
+   */
+  public function onHtml(GetResponseEvent $event) {
+    $page_callback_result = $event->getControllerResult();
+    return new Response(drupal_render_page($page_callback_result));
+  }
+
+  /**
+   * Registers the methods in this class that should be listeners.
+   *
+   * @return array
+   *   An array of event listener definitions.
+   */
+  static function getSubscribedEvents() {
+    $events[KernelEvents::VIEW][] = array('onView');
+
+    return $events;
+  }
+}
diff --git a/core/lib/Drupal/Core/ExceptionController.php b/core/lib/Drupal/Core/ExceptionController.php
new file mode 100644
index 0000000..a2ab913
--- /dev/null
+++ b/core/lib/Drupal/Core/ExceptionController.php
@@ -0,0 +1,411 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\ExceptionController.
+ */
+
+namespace Drupal\Core;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpFoundation\JsonResponse;
+
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\HttpKernel\Controller\ControllerResolver;
+use Symfony\Component\HttpKernel\Exception\FlattenException;
+
+use Exception;
+
+/**
+ * This controller handles HTTP errors generated by the routing system.
+ */
+class ExceptionController {
+
+  /**
+   * The kernel that spawned this controller.
+   *
+   * We will use this to fire subrequests as needed.
+   *
+   * @var Symfony\Component\HttpKernel\HttpKernelInterface
+   */
+  protected $kernel;
+
+  /**
+   * The content negotiation library.
+   *
+   * @var Drupal\Core\ContentNegotiation
+   */
+  protected $negotiation;
+
+  /**
+   * Constructor.
+   *
+   * @param Symfony\Component\HttpKernel\HttpKernelInterface $kernel
+   *   The kernel that spawned this controller, so that it can be reused
+   *   for subrequests.
+   * @param Drupal\Core\ContentNegotiation $negotiation
+   *   The content negotiation library to use to determine the correct response
+   *   format.
+   */
+  public function __construct(HttpKernelInterface $kernel, ContentNegotiation $negotiation) {
+    $this->kernel = $kernel;
+    $this->negotiation = $negotiation;
+  }
+
+  /**
+   * Handles an exception on a request.
+   *
+   * @param Symfony\Component\HttpKernel\Exception\FlattenException $exception
+   *   The flattened exception.
+   * @param Symfony\Component\HttpFoundation\Request $request
+   *   The request that generated the exception.
+   *
+   * @return Symfony\Component\HttpFoundation\Response
+   *   A response object to be sent to the server.
+   */
+  public function execute(FlattenException $exception, Request $request) {
+    $method = 'on' . $exception->getStatusCode() . $this->negotiation->getContentType($request);
+
+    if (method_exists($this, $method)) {
+      return $this->$method($exception, $request);
+    }
+
+    return new Response('A fatal error occurred: ' . $exception->getMessage(), $exception->getStatusCode());
+  }
+
+  /**
+   * Processes a MethodNotAllowed exception into an HTTP 405 response.
+   *
+   * @param Symfony\Component\HttpKernel\Exception\FlattenException $exception
+   *   The flattened exception.
+   * @param Symfony\Component\HttpFoundation\Request $request
+   *   The request object that triggered this exception.
+   */
+  public function on405Html(FlattenException $exception, Request $request) {
+    $event->setResponse(new Response('Method Not Allowed', 405));
+  }
+
+  /**
+   * Processes an AccessDenied exception into an HTTP 403 response.
+   *
+   * @param Symfony\Component\HttpKernel\Exception\FlattenException $exception
+   *   The flattened exception.
+   * @param Symfony\Component\HttpFoundation\Request $request
+   *   The request object that triggered this exception.
+   */
+  public function on403Html(FlattenException $exception, Request $request) {
+    $system_path = $request->attributes->get('system_path');
+    watchdog('access denied', $system_path, NULL, WATCHDOG_WARNING);
+
+    $path = drupal_get_normal_path(variable_get('site_403', ''));
+    if ($path && $path != $system_path) {
+      // Keep old path for reference, and to allow forms to redirect to it.
+      if (!isset($_GET['destination'])) {
+        $_GET['destination'] = $system_path;
+      }
+
+      $subrequest = Request::create('/' . $path, 'get', array('destination' => $system_path), $request->cookies->all(), array(), $request->server->all());
+
+      // The active trail is being statically cached from the parent request to
+      // the subrequest, like any other static.  Unfortunately that means the
+      // data in it is incorrect and does not get regenerated correctly for
+      // the subrequest.  In this instance, that even causes a fatal error in
+      // some circumstances because menu_get_active_trail() ends up having
+      // a missing localized_options value.  To work around that, reset the
+      // menu static variables and let them be regenerated as needed.
+      // @todo It is likely that there are other such statics that need to be
+      //   reset that are not triggering test failures right now.  If found,
+      //   add them here.
+      // @todo Refactor the breadcrumb system so that it does not rely on static
+      //   variables in the first place, which will eliminate the need for this
+      //   hack.
+      drupal_static_reset('menu_set_active_trail');
+      menu_reset_static_cache();
+
+      $response = $this->kernel->handle($subrequest, DrupalKernel::SUB_REQUEST);
+      $response->setStatusCode(403, 'Access denied');
+    }
+    else {
+      $response = new Response('Access Denied', 403);
+
+      // @todo Replace this block with something cleaner.
+      $return = t('You are not authorized to access this page.');
+      drupal_set_title(t('Access denied'));
+      drupal_set_page_content($return);
+      $page = element_info('page');
+      $content = drupal_render_page($page);
+
+      $response->setContent($content);
+    }
+
+    return $response;
+  }
+
+  /**
+   * Processes a NotFound exception into an HTTP 404 response.
+   *
+   * @param Symfony\Component\HttpKernel\Exception\FlattenException $exception
+   *   The flattened exception.
+   * @param Sonfony\Component\HttpFoundation\Request $request
+   *   The request object that triggered this exception.
+   */
+  public function on404Html(FlattenException $exception, Request $request) {
+    watchdog('page not found', check_plain($request->attributes->get('system_path')), NULL, WATCHDOG_WARNING);
+
+    // Check for and return a fast 404 page if configured.
+    // @todo Inline this rather than using a function.
+    drupal_fast_404();
+
+    $system_path = $request->attributes->get('system_path');
+
+    // Keep old path for reference, and to allow forms to redirect to it.
+    if (!isset($_GET['destination'])) {
+      $_GET['destination'] = $system_path;
+    }
+
+    $path = drupal_get_normal_path(variable_get('site_404', ''));
+    if ($path && $path != $system_path) {
+      // @todo Um, how do I specify an override URL again? Totally not clear. Do
+      //   that and sub-call the kernel rather than using meah().
+      // @todo The create() method expects a slash-prefixed path, but we store a
+      //   normal system path in the site_404 variable.
+      $subrequest = Request::create('/' . $path, 'get', array(), $request->cookies->all(), array(), $request->server->all());
+
+      // The active trail is being statically cached from the parent request to
+      // the subrequest, like any other static.  Unfortunately that means the
+      // data in it is incorrect and does not get regenerated correctly for
+      // the subrequest.  In this instance, that even causes a fatal error in
+      // some circumstances because menu_get_active_trail() ends up having
+      // a missing localized_options value.  To work around that, reset the
+      // menu static variables and let them be regenerated as needed.
+      // @todo It is likely that there are other such statics that need to be
+      //   reset that are not triggering test failures right now.  If found,
+      //   add them here.
+      // @todo Refactor the breadcrumb system so that it does not rely on static
+      //   variables in the first place, which will eliminate the need for this
+      //   hack.
+      drupal_static_reset('menu_set_active_trail');
+      menu_reset_static_cache();
+
+      $response = $this->kernel->handle($subrequest, HttpKernelInterface::SUB_REQUEST);
+      $response->setStatusCode(404, 'Not Found');
+    }
+    else {
+      $response = new Response('Not Found', 404);
+
+      // @todo Replace this block with something cleaner.
+      $return = t('The requested page "@path" could not be found.', array('@path' => $request->getPathInfo()));
+      drupal_set_title(t('Page not found'));
+      drupal_set_page_content($return);
+      $page = element_info('page');
+      $content = drupal_render_page($page);
+
+      $response->setContent($content);
+    }
+
+    return $response;
+  }
+
+  /**
+   * Processes a generic exception into an HTTP 500 response.
+   *
+   * @param Symfony\Component\HttpKernel\Exception\FlattenException $exception
+   *   Metadata about the exception that was thrown.
+   * @param Symfony\Component\HttpFoundation\Request $request
+   *   The request object that triggered this exception.
+   */
+  public function on500Html(FlattenException $exception, Request $request) {
+    $error = $this->decodeException($exception);
+
+    // Because the kernel doesn't run until full bootstrap, we know that
+    // most subsystems are already initialized.
+
+    $headers = array();
+
+    // When running inside the testing framework, we relay the errors
+    // to the tested site by the way of HTTP headers.
+    $test_info = &$GLOBALS['drupal_test_info'];
+    if (!empty($test_info['in_child_site']) && !headers_sent() && (!defined('SIMPLETEST_COLLECT_ERRORS') || SIMPLETEST_COLLECT_ERRORS)) {
+      // $number does not use drupal_static as it should not be reset
+      // as it uniquely identifies each PHP error.
+      static $number = 0;
+      $assertion = array(
+        $error['!message'],
+        $error['%type'],
+        array(
+          'function' => $error['%function'],
+          'file' => $error['%file'],
+          'line' => $error['%line'],
+        ),
+      );
+      $headers['X-Drupal-Assertion-' . $number] = rawurlencode(serialize($assertion));
+      $number++;
+    }
+
+    watchdog('php', '%type: !message in %function (line %line of %file).', $error, $error['severity_level']);
+
+    // Display the message if the current error reporting level allows this type
+    // of message to be displayed, and unconditionnaly in update.php.
+    if (error_displayable($error)) {
+      $class = 'error';
+
+      // If error type is 'User notice' then treat it as debug information
+      // instead of an error message, see dd().
+      if ($error['%type'] == 'User notice') {
+        $error['%type'] = 'Debug';
+        $class = 'status';
+      }
+
+      drupal_set_message(t('%type: !message in %function (line %line of %file).', $error), $class);
+    }
+
+    drupal_set_title(t('Error'));
+    // We fallback to a maintenance page at this point, because the page
+    // generation itself can generate errors.
+    $output = theme('maintenance_page', array('content' => t('The website encountered an unexpected error. Please try again later.')));
+
+    $response = new Response($output, 500);
+    $response->setStatusCode(500, '500 Service unavailable (with message)');
+
+    return $response;
+  }
+
+  /**
+   * Processes an AccessDenied exception that occured on a JSON request.
+   *
+   * @param Symfony\Component\HttpKernel\Exception\FlattenException $exception
+   *   The flattened exception.
+   * @param Symfony\Component\HttpFoundation\Request $request
+   *   The request object that triggered this exception.
+   */
+  public function on403Json(FlattenException $exception, Request $request) {
+    $response = new JsonResponse();
+    $response->setStatusCode(403, 'Access Denied');
+    return $response;
+  }
+
+  /**
+   * Processes a NotFound exception that occured on a JSON request.
+   *
+   * @param Symfony\Component\HttpKernel\Exception\FlattenException $exception
+   *   The flattened exception.
+   * @param Symfony\Component\HttpFoundation\Request $request
+   *   The request object that triggered this exception.
+   */
+  public function on404Json(FlattenException $exception, Request $request) {
+    $response = new JsonResponse();
+    $response->setStatusCode(404, 'Not Found');
+    return $response;
+  }
+
+  /**
+   * Processes a MethodNotAllowed exception that occured on a JSON request.
+   *
+   * @param Symfony\Component\HttpKernel\Exception\FlattenException $exception
+   *   The flattened exception.
+   * @param Symfony\Component\HttpFoundation\Request $request
+   *   The request object that triggered this exception.
+   */
+  public function on405Json(FlattenException $exception, Request $request) {
+    $response = new JsonResponse();
+    $response->setStatusCode(405, 'Method Not Allowed');
+    return $response;
+  }
+
+
+  /**
+   * This method is a temporary port of _drupal_decode_exception().
+   *
+   * @todo This should get refactored. FlattenException could use some
+   *   improvement as well.
+   *
+   * @return array
+   */
+  protected function decodeException(FlattenException $exception) {
+    $message = $exception->getMessage();
+
+    $backtrace = $exception->getTrace();
+
+    // This value is missing from the stack for some reason in the
+    // FlattenException version of the backtrace.
+    $backtrace[0]['line'] = $exception->getLine();
+
+    // For database errors, we try to return the initial caller,
+    // skipping internal functions of the database layer.
+    if (strpos($exception->getClass(), 'DatabaseExceptionWrapper') !== FALSE) {
+      // A DatabaseExceptionWrapper exception is actually just a courier for
+      // the original PDOException.  It's the stack trace from that exception
+      // that we care about.
+      $backtrace = $exception->getPrevious()->getTrace();
+      $backtrace[0]['line'] = $exception->getLine();
+
+      // The first element in the stack is the call, the second element gives us the caller.
+      // We skip calls that occurred in one of the classes of the database layer
+      // or in one of its global functions.
+      $db_functions = array('db_query',  'db_query_range');
+      while (!empty($backtrace[1]) && ($caller = $backtrace[1]) &&
+          ((strpos($caller['namespace'], 'Drupal\Core\Database') !== FALSE || strpos($caller['class'], 'PDO') !== FALSE)) ||
+          in_array($caller['function'], $db_functions)) {
+        // We remove that call.
+        array_shift($backtrace);
+      }
+    }
+    $caller = $this->getLastCaller($backtrace);
+
+    return array(
+      '%type' => $exception->getClass(),
+      // The standard PHP exception handler considers that the exception message
+      // is plain-text. We mimick this behavior here.
+      '!message' => check_plain($message),
+      '%function' => $caller['function'],
+      '%file' => $caller['file'],
+      '%line' => $caller['line'],
+      'severity_level' => WATCHDOG_ERROR,
+    );
+  }
+
+  /**
+   * Gets the last caller from a backtrace.
+   *
+   * The last caller is not necessarily the first item in the backtrace. Rather,
+   * it is the first item in the backtrace that is a PHP userspace function,
+   * and not one of our debug functions.
+   *
+   * @param $backtrace
+   *   A standard PHP backtrace.
+   *
+   * @return
+   *   An associative array with keys 'file', 'line' and 'function'.
+   */
+  protected function getLastCaller($backtrace) {
+    // Ignore black listed error handling functions.
+    $blacklist = array('debug', '_drupal_error_handler', '_drupal_exception_handler');
+
+    // Errors that occur inside PHP internal functions do not generate
+    // information about file and line.
+    while (($backtrace && !isset($backtrace[0]['line'])) ||
+          (isset($backtrace[1]['function']) && in_array($backtrace[1]['function'], $blacklist))) {
+      array_shift($backtrace);
+    }
+
+    // The first trace is the call itself.
+    // It gives us the line and the file of the last call.
+    $call = $backtrace[0];
+
+    // The second call give us the function where the call originated.
+    if (isset($backtrace[1])) {
+      if (isset($backtrace[1]['class'])) {
+        $call['function'] = $backtrace[1]['class'] . $backtrace[1]['type'] . $backtrace[1]['function'] . '()';
+      }
+      else {
+        $call['function'] = $backtrace[1]['function'] . '()';
+      }
+    }
+    else {
+      $call['function'] = 'main()';
+    }
+    return $call;
+  }
+}
diff --git a/core/lib/Drupal/Core/UrlMatcher.php b/core/lib/Drupal/Core/UrlMatcher.php
new file mode 100644
index 0000000..57b6317
--- /dev/null
+++ b/core/lib/Drupal/Core/UrlMatcher.php
@@ -0,0 +1,168 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\UrlMatcher.
+ */
+
+namespace Drupal\Core;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
+use Symfony\Component\Routing\Matcher\UrlMatcher as SymfonyUrlMatcher;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * UrlMatcher matches URL based on a set of routes.
+ */
+class UrlMatcher implements UrlMatcherInterface {
+
+  /**
+   * The request context for this matcher.
+   *
+   * @var Symfony\Component\Routing\RequestContext
+   */
+  protected $context;
+
+  /**
+   * The request object for this matcher.
+   *
+   * @var Symfony\Component\HttpFoundation\Request
+   */
+  protected $request;
+
+  /**
+   * 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;
+  }
+
+  /**
+   * Sets the request object to use.
+   *
+   * This is used by the RouterListener to make additional request attributes
+   * available.
+   *
+   * @param Symfony\Component\HttpFoundation\Request $request
+   *   The request object.
+   */
+  public function setRequest(Request $request) {
+    $this->request = $request;
+  }
+
+  /**
+   * Gets the request object.
+   *
+   * @return Symfony\Component\HttpFoundation\Request $request
+   *   The request object.
+   */
+  public function getRequest() {
+    return $this->request;
+  }
+
+  /**
+   * {@inheritDoc}
+   *
+   * @api
+   */
+  public function match($pathinfo) {
+    if ($router_item = $this->matchDrupalItem($pathinfo)) {
+      $ret = $this->convertDrupalItem($router_item);
+      // Stash the router item in the attributes while we're transitioning.
+      $ret['drupal_menu_item'] = $router_item;
+
+      // Most legacy controllers (aka page callbacks) are in a separate file,
+      // so we have to include that.
+      if ($router_item['include_file']) {
+        require_once DRUPAL_ROOT . '/' . $router_item['include_file'];
+      }
+
+      return $ret;
+    }
+
+    // This matcher doesn't differentiate by method, so don't bother with those
+    // exceptions.
+    throw new ResourceNotFoundException();
+  }
+
+  /**
+   * Get a Drupal menu item.
+   *
+   * @todo Make this return multiple possible candidates for the resolver to
+   *   consider.
+   *
+   * @param string $path
+   *   The path being looked up by
+   */
+  protected function matchDrupalItem($path) {
+    // For now we can just proxy our procedural method. At some point this will
+    // become more complicated because we'll need to get back candidates for a
+    // path and them resolve them based on things like method and scheme which
+    // we currently can't do.
+    return menu_get_item($path);
+  }
+
+  /**
+   * Converts a Drupal menu item to a route array.
+   *
+   * @param array $router_item
+   *   The Drupal menu item.
+   *
+   * @return
+   *   An array of parameters.
+   */
+  protected function convertDrupalItem($router_item) {
+    $route = array(
+      '_controller' => $router_item['page_callback']
+    );
+
+    // @todo menu_get_item() does not unserialize page arguments when the access
+    //   is denied. Remove this temporary hack that always does that.
+    if (!is_array($router_item['page_arguments'])) {
+      $router_item['page_arguments'] = unserialize($router_item['page_arguments']);
+    }
+
+    // Place argument defaults on the route.
+    foreach ($router_item['page_arguments'] as $k => $v) {
+      $route[$k] = $v;
+    }
+    return $route;
+  }
+}
diff --git a/index.php b/index.php
index b91fb1e..8a36c6e 100644
--- a/index.php
+++ b/index.php
@@ -18,4 +18,11 @@ define('DRUPAL_ROOT', getcwd());
 
 require_once DRUPAL_ROOT . '/core/includes/bootstrap.inc';
 drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
+
+$tmp_classes = unserialize('a:42:{s:40:"Symfony\Component\HttpFoundation\Request";s:56:"core/vendor/Symfony/Component/HttpFoundation/Request.php";s:45:"Symfony\Component\HttpFoundation\ParameterBag";s:61:"core/vendor/Symfony/Component/HttpFoundation/ParameterBag.php";s:40:"Symfony\Component\HttpFoundation\FileBag";s:56:"core/vendor/Symfony/Component/HttpFoundation/FileBag.php";s:42:"Symfony\Component\HttpFoundation\ServerBag";s:58:"core/vendor/Symfony/Component/HttpFoundation/ServerBag.php";s:42:"Symfony\Component\HttpFoundation\HeaderBag";s:58:"core/vendor/Symfony/Component/HttpFoundation/HeaderBag.php";s:58:"Symfony\Component\EventDispatcher\EventDispatcherInterface";s:74:"core/vendor/Symfony/Component/EventDispatcher/EventDispatcherInterface.php";s:49:"Symfony\Component\EventDispatcher\EventDispatcher";s:65:"core/vendor/Symfony/Component/EventDispatcher/EventDispatcher.php";s:67:"Symfony\Component\HttpKernel\Controller\ControllerResolverInterface";s:83:"core/vendor/Symfony/Component/HttpKernel/Controller/ControllerResolverInterface.php";s:58:"Symfony\Component\HttpKernel\Controller\ControllerResolver";s:74:"core/vendor/Symfony/Component/HttpKernel/Controller/ControllerResolver.php";s:48:"Symfony\Component\HttpKernel\HttpKernelInterface";s:64:"core/vendor/Symfony/Component/HttpKernel/HttpKernelInterface.php";s:48:"Symfony\Component\HttpKernel\TerminableInterface";s:64:"core/vendor/Symfony/Component/HttpKernel/TerminableInterface.php";s:39:"Symfony\Component\HttpKernel\HttpKernel";s:55:"core/vendor/Symfony/Component/HttpKernel/HttpKernel.php";s:24:"Drupal\Core\DrupalKernel";s:37:"core/lib/Drupal/Core/DrupalKernel.php";s:54:"Symfony\Component\Routing\RequestContextAwareInterface";s:70:"core/vendor/Symfony/Component/Routing/RequestContextAwareInterface.php";s:53:"Symfony\Component\Routing\Matcher\UrlMatcherInterface";s:69:"core/vendor/Symfony/Component/Routing/Matcher/UrlMatcherInterface.php";s:22:"Drupal\Core\UrlMatcher";s:35:"core/lib/Drupal/Core/UrlMatcher.php";s:40:"Symfony\Component\Routing\RequestContext";s:56:"core/vendor/Symfony/Component/Routing/RequestContext.php";s:58:"Symfony\Component\EventDispatcher\EventSubscriberInterface";s:74:"core/vendor/Symfony/Component/EventDispatcher/EventSubscriberInterface.php";s:57:"Symfony\Component\HttpKernel\EventListener\RouterListener";s:73:"core/vendor/Symfony/Component/HttpKernel/EventListener/RouterListener.php";s:42:"Drupal\Core\EventSubscriber\RouterListener";s:55:"core/lib/Drupal/Core/EventSubscriber/RouterListener.php";s:41:"Symfony\Component\HttpKernel\KernelEvents";s:57:"core/vendor/Symfony/Component/HttpKernel/KernelEvents.php";s:30:"Drupal\Core\ContentNegotiation";s:43:"core/lib/Drupal/Core/ContentNegotiation.php";s:42:"Drupal\Core\EventSubscriber\ViewSubscriber";s:55:"core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php";s:44:"Drupal\Core\EventSubscriber\AccessSubscriber";s:57:"core/lib/Drupal/Core/EventSubscriber/AccessSubscriber.php";s:53:"Drupal\Core\EventSubscriber\MaintenanceModeSubscriber";s:66:"core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php";s:44:"Drupal\Core\EventSubscriber\PathListenerBase";s:57:"core/lib/Drupal/Core/EventSubscriber/PathListenerBase.php";s:42:"Drupal\Core\EventSubscriber\PathSubscriber";s:55:"core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php";s:51:"Drupal\Core\EventSubscriber\LegacyRequestSubscriber";s:64:"core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php";s:54:"Drupal\Core\EventSubscriber\LegacyControllerSubscriber";s:67:"core/lib/Drupal/Core/EventSubscriber/LegacyControllerSubscriber.php";s:52:"Drupal\Core\EventSubscriber\FinishResponseSubscriber";s:65:"core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php";s:50:"Drupal\Core\EventSubscriber\RequestCloseSubscriber";s:63:"core/lib/Drupal/Core/EventSubscriber/RequestCloseSubscriber.php";s:60:"Symfony\Component\HttpKernel\EventListener\ExceptionListener";s:76:"core/vendor/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php";s:31:"Drupal\Core\ExceptionController";s:44:"core/lib/Drupal/Core/ExceptionController.php";s:39:"Symfony\Component\EventDispatcher\Event";s:55:"core/vendor/Symfony/Component/EventDispatcher/Event.php";s:46:"Symfony\Component\HttpKernel\Event\KernelEvent";s:62:"core/vendor/Symfony/Component/HttpKernel/Event/KernelEvent.php";s:51:"Symfony\Component\HttpKernel\Event\GetResponseEvent";s:67:"core/vendor/Symfony/Component/HttpKernel/Event/GetResponseEvent.php";s:56:"Symfony\Component\HttpKernel\Event\FilterControllerEvent";s:72:"core/vendor/Symfony/Component/HttpKernel/Event/FilterControllerEvent.php";s:70:"Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent";s:86:"core/vendor/Symfony/Component/HttpKernel/Event/GetResponseForControllerResultEvent.php";s:41:"Symfony\Component\HttpFoundation\Response";s:57:"core/vendor/Symfony/Component/HttpFoundation/Response.php";s:50:"Symfony\Component\HttpFoundation\ResponseHeaderBag";s:66:"core/vendor/Symfony/Component/HttpFoundation/ResponseHeaderBag.php";s:54:"Symfony\Component\HttpKernel\Event\FilterResponseEvent";s:70:"core/vendor/Symfony/Component/HttpKernel/Event/FilterResponseEvent.php";s:52:"Symfony\Component\HttpKernel\Event\PostResponseEvent";s:68:"core/vendor/Symfony/Component/HttpKernel/Event/PostResponseEvent.php";}');
+foreach ($tmp_classes as $tmp_class => $tmp_file) {
+  require DRUPAL_ROOT . '/' . $tmp_file;
+}
+
 menu_execute_active_handler();
+
