diff --git a/.htaccess b/.htaccess
index a69bdd4..725897e 100644
--- a/.htaccess
+++ b/.htaccess
@@ -109,7 +109,7 @@ DirectoryIndex index.php index.html index.htm
   RewriteCond %{REQUEST_FILENAME} !-f
   RewriteCond %{REQUEST_FILENAME} !-d
   RewriteCond %{REQUEST_URI} !=/favicon.ico
-  RewriteRule ^ index.php [L]
+  RewriteRule ^(.*)$ index.php [L]
 
   # Rules to correctly serve gzip compressed CSS and JS files.
   # Requires both mod_rewrite and mod_headers to be enabled.
diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index c26ec86..7673d23 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -3,6 +3,7 @@
 use Drupal\Core\Database\Database;
 use Symfony\Component\ClassLoader\UniversalClassLoader;
 use Symfony\Component\ClassLoader\ApcUniversalClassLoader;
+use Symfony\Component\HttpFoundation\Request;
 
 /**
  * @file
@@ -1510,6 +1511,27 @@ function request_uri() {
 }
 
 /**
+ * Returns the current global reuqest object.
+ *
+ * @todo Replace this function with a proper dependency injection container.
+ *
+ * @staticvar Request $request
+ * @param Request $new_request
+ *   The new request object to store. If you are not index.php, you probably
+ *   should not be using this parameter.
+ * @return Request
+ *   The current request object.
+ */
+function request(Request $new_request = NULL) {
+  static $request;
+
+  if ($new_request) {
+    $request = $new_request;
+  }
+  return $request;
+}
+
+/**
  * Logs an exception.
  *
  * This is a wrapper function for watchdog() which automatically decodes an
diff --git a/core/includes/common.inc b/core/includes/common.inc
index a18e1e4..c15bd1c 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -1,5 +1,7 @@
 <?php
 
+use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 use Drupal\Core\Database\Database;
 
 /**
@@ -711,7 +713,11 @@ function drupal_site_offline() {
  * bubble up to menu_execute_active_handler() should call drupal_not_found().
  */
 function drupal_not_found() {
-  drupal_deliver_page(MENU_NOT_FOUND);
+
+  throw new NotFoundHttpException();
+
+  // @todo Remove this line.
+  //drupal_deliver_page(MENU_NOT_FOUND);
 }
 
 /**
@@ -724,7 +730,11 @@ function drupal_not_found() {
  * drupal_access_denied().
  */
 function drupal_access_denied() {
-  drupal_deliver_page(MENU_ACCESS_DENIED);
+
+  throw new AccessDeniedException();
+
+  // @todo Remove this line.
+  //drupal_deliver_page(MENU_ACCESS_DENIED);
 }
 
 /**
@@ -2200,31 +2210,20 @@ function url($path = NULL, array $options = array()) {
   $base = $options['absolute'] ? $options['base_url'] . '/' : base_path();
   $prefix = empty($path) ? rtrim($options['prefix'], '/') : $options['prefix'];
 
-  // With Clean URLs.
-  if (!empty($GLOBALS['conf']['clean_url'])) {
-    $path = drupal_encode_path($prefix . $path);
-    if ($options['query']) {
-      return $base . $path . '?' . drupal_http_build_query($options['query']) . $options['fragment'];
-    }
-    else {
-      return $base . $path . $options['fragment'];
-    }
+  // If Clean URLs are not enabled, we need to prefix the script name onto
+  // the link.
+  // @todo: Make this dynamic based on the request object without using a global
+  // request object.
+  if (empty($GLOBALS['conf']['clean_url'])) {
+    $base .= 'index.php/';
+  }
+
+  $path = drupal_encode_path($prefix . $path);
+  if ($options['query']) {
+    return $base . $path . '?' . drupal_http_build_query($options['query']) . $options['fragment'];
   }
-  // Without Clean URLs.
   else {
-    $path = $prefix . $path;
-    $query = array();
-    if (!empty($path)) {
-      $query['q'] = $path;
-    }
-    if ($options['query']) {
-      // We do not use array_merge() here to prevent overriding $path via query
-      // parameters.
-      $query += $options['query'];
-    }
-    $query = $query ? ('?' . drupal_http_build_query($query)) : '';
-    $script = isset($options['script']) ? $options['script'] : '';
-    return $base . $script . $query . $options['fragment'];
+    return $base . $path . $options['fragment'];
   }
 }
 
diff --git a/core/includes/file.inc b/core/includes/file.inc
index eaff634..fa6670d 100644
--- a/core/includes/file.inc
+++ b/core/includes/file.inc
@@ -5,6 +5,9 @@
  * API for handling file uploads and server file management.
  */
 
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Symfony\Component\HttpFoundation\StreamedResponse;
 use Drupal\Core\StreamWrapper\LocalStream;
 
 /**
@@ -2046,18 +2049,27 @@ function file_download() {
       $function = $module . '_file_download';
       $result = $function($uri);
       if ($result == -1) {
-        return drupal_access_denied();
+        throw new AccessDeniedHttpException();
       }
       if (isset($result) && is_array($result)) {
         $headers = array_merge($headers, $result);
       }
     }
     if (count($headers)) {
-      file_transfer($uri, $headers);
+      return new StreamedResponse(function() use ($uri) {
+        $scheme = file_uri_scheme($uri);
+        // Transfer file in 1024 byte chunks to save memory usage.
+        if ($scheme && file_stream_wrapper_valid_scheme($scheme) && $fd = fopen($uri, 'rb')) {
+          while (!feof($fd)) {
+            print fread($fd, 1024);
+          }
+          fclose($fd);
+        }
+      }, 200, $headers);
     }
-    return drupal_access_denied();
+    throw new AccessDeniedHttpException();
   }
-  return drupal_not_found();
+  throw new NotFoundHttpException();
 }
 
 
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 07e25a0..019d78a 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -3,6 +3,8 @@
 use Drupal\Core\Database\Database;
 use Drupal\Core\Database\Install\TaskException;
 
+use Symfony\Component\HttpFoundation\Request;
+
 /**
  * @file
  * API functions for installing Drupal.
@@ -261,6 +263,14 @@ function install_begin_request(&$install_state) {
 
   drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
 
+  // A request object from the HTTPFoundation to tell us about the request.
+  $request = Request::createFromGlobals();
+
+  // Set the global $request object.  This is a temporary measure to
+  // keep legacy utility functions working.  It should be moved to a dependency
+  // injection container at some point.
+  request($request);
+
   // This must go after drupal_bootstrap(), which unsets globals!
   global $conf;
 
diff --git a/core/lib/Drupal/Core/ContentNegotiation.php b/core/lib/Drupal/Core/ContentNegotiation.php
new file mode 100644
index 0000000..29484ba
--- /dev/null
+++ b/core/lib/Drupal/Core/ContentNegotiation.php
@@ -0,0 +1,42 @@
+<?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 {
+
+  /**
+   * Returns the normalized type of a given request.
+   *
+   * The normalized type is a short, lowercase version of the format, such as
+   * "html" or "json" or "atom".
+   *
+   * @param Request $request
+   *   The request object from which to extract the content type.
+   */
+  public function getContentType(Request $request) {
+    $acceptable_content_types = $request->getAcceptableContentTypes();
+    if (in_array('application/json', $acceptable_content_types)) {
+      return 'json';
+    }
+
+    // Do HTML last so that it always wins for */* formats.
+    if(in_array('text/html', $acceptable_content_types) || in_array('*/*', $acceptable_content_types)) {
+      return 'html';
+    }
+  }
+
+}
+
diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php
new file mode 100644
index 0000000..3899802
--- /dev/null
+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -0,0 +1,84 @@
+<?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\RouterListener;
+use Symfony\Component\HttpKernel\EventListener\ExceptionListener;
+use Drupal\Core\EventSubscriber\ViewSubscriber;
+use Drupal\Core\EventSubscriber\AccessSubscriber;
+use Drupal\Core\EventSubscriber\PathSubscriber;
+use Drupal\Core\EventSubscriber\LegacyControllerSubscriber;
+
+use Exception;
+
+/**
+ * The DrupalKernel class is the core of Drupal itself.
+ */
+class DrupalKernel extends HttpKernel {
+
+    /**
+     * The event dispatcher used by this kernel.
+     *
+     * @var EventDispatcherInterface
+     */
+    protected $dispatcher;
+
+    /**
+     * The controller resolver that will extract the controller from a Request.
+     *
+     * @var ControllerResolverInterface
+     */
+    protected $resolver;
+
+
+    /**
+     * Constructor
+     *
+     * @param EventDispatcherInterface $dispatcher
+     *   An EventDispatcherInterface instance
+     * @param ControllerResolverInterface $resolver
+     *   A ControllerResolverInterface instance
+     */
+   public function __construct(EventDispatcherInterface $dispatcher, ControllerResolverInterface $resolver) {
+      parent::__construct($dispatcher, $resolver);
+      $this->dispatcher = $dispatcher;
+      $this->resolver = $resolver;
+
+      $context = new RequestContext();
+      $this->matcher = new UrlMatcher($context);
+      $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 PathSubscriber());
+      $this->dispatcher->addSubscriber(new LegacyControllerSubscriber());
+
+      // 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..04eac0a
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/AccessSubscriber.php
@@ -0,0 +1,54 @@
+<?php
+
+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;
+
+/**
+ * @file
+ *
+ * Definition of Drupal\Core\EventSubscriber\AccessSubscriber
+ */
+
+/**
+ * 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 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/LegacyControllerSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/LegacyControllerSubscriber.php
new file mode 100644
index 0000000..cc4da15
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/LegacyControllerSubscriber.php
@@ -0,0 +1,64 @@
+<?php
+
+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;
+
+/**
+ * @file
+ *
+ * Definition of Drupal\Core\EventSubscriber\LegacyControllerSubscriber
+ */
+
+/**
+ * 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 GetResponseEvent $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/PathSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php
new file mode 100644
index 0000000..dc5059b
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php
@@ -0,0 +1,68 @@
+<?php
+
+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;
+
+/**
+ * @file
+ *
+ * Definition of Drupal\Core\EventSubscriber\AccessSubscriber
+ */
+
+/**
+ * Access subscriber for controller requests.
+ */
+class PathSubscriber implements EventSubscriberInterface {
+
+  /**
+   * Resolve the system path.
+   *
+   * @todo The path system should be objectified to remove the function calls
+   * in this method.
+   *
+   * @todo We're writing back to $_GET['q'] for temporary BC. All instances of
+   * $_GET['q'] should be removed and then this code eliminated.
+   *
+   * @param GetResponseEvent $event
+   *   The Event to process.
+   */
+  public function onKernelRequestPathResolve(GetResponseEvent $event) {
+
+    $request = $event->getRequest();
+
+    $path = ltrim($request->getPathInfo(), '/');
+
+    if (empty($path)) {
+      // @todo Temporary hack. Fix when configuration is injectable.
+      $path = variable_get('site_frontpage', 'user');
+    }
+    $system_path = drupal_get_normal_path($path);
+
+    $request->attributes->set('system_path', $system_path);
+
+    // @todo Remove this line.
+    // Drupal uses $_GET['q'] directly in over 100 places at present,
+    // including writing back to it at times. Those are all critical bugs,
+    // even by Drupal 7 standards, but as many of the places that it does so
+    // are slated to be rewritten anyway we will save time and include this
+    // temporary hack. Removal of this line is a critical, Drupal-release
+    // blocking bug.
+    $_GET['q'] = $system_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('onKernelRequestPathResolve', 100);
+
+    return $events;
+  }
+}
diff --git a/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php
new file mode 100644
index 0000000..a8745767
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php
@@ -0,0 +1,99 @@
+<?php
+
+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;
+
+/**
+ * @file
+ *
+ * Definition of Drupal\Core\EventSubscriber\ViewSubscriber;
+ */
+
+/**
+ * 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 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();
+
+    //print_r($page_callback_result);
+
+    $response = new JsonResponse();
+    $response->setContent($page_callback_result);
+
+    return $response;
+  }
+
+  /**
+   * 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 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..1c2d75e
--- /dev/null
+++ b/core/lib/Drupal/Core/ExceptionController.php
@@ -0,0 +1,209 @@
+<?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 HttpKernelInterface
+   */
+  protected $kernel;
+
+  /**
+   * The
+   *
+   * @var ContentNegotiation
+   */
+  protected $negotiation;
+
+  /**
+   * Constructor
+   *
+   * @param HttpKernelInterface $kernel
+   *   The kernel that spawned this controller, so that it can be reused
+   *   for subrequests.
+   * @param 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 FlattenException $exception
+   *   The flattened exception.
+   * @param 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 GetResponseEvent $event
+   *   The Event to process.
+   */
+  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 GetResponseEvent $event
+   *   The Event to process.
+   */
+  public function on403Html(FlattenException $exception, Request $request) {
+    $system_path = $request->attributes->get('system_path');
+    $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());
+
+      $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 403 response.
+   *
+   * @param GetResponseEvent $event
+   *   The Event to process.
+   */
+  public function on404Html(FlattenException $exception, Request $request) {
+    watchdog('page not found', check_plain($_GET['q']), 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());
+
+      $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 an AccessDenied exception into an HTTP 403 response.
+   *
+   * @param GetResponseEvent $event
+   *   The Event to process.
+   */
+  public function on403Json(FlattenException $exception, Request $request) {
+    $response = new JsonResponse();
+    $response->setStatusCode(403, 'Access Denied');
+    return $response;
+  }
+
+  /**
+   * Processes a NotFound exception into an HTTP 404 response.
+   *
+   * @param GetResponseEvent $event
+   *   The Event to process.
+   */
+  public function on404Json(FlattenException $exception, Request $request) {
+    $response = new JsonResponse();
+    $response->setStatusCode(404, 'Not Found');
+    return $response;
+  }
+
+  /**
+   * Processes a MethodNotAllowed exception into an HTTP 405 response.
+   *
+   * @param GetResponseEvent $event
+   *   The Event to process.
+   */
+  public function on405Json(FlattenException $exception, Request $request) {
+    $response = new JsonResponse();
+    $response->setStatusCode(405, 'Method Not Allowed');
+    return $response;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/UrlMatcher.php b/core/lib/Drupal/Core/UrlMatcher.php
new file mode 100644
index 0000000..2dd9d25
--- /dev/null
+++ b/core/lib/Drupal/Core/UrlMatcher.php
@@ -0,0 +1,109 @@
+<?php
+
+/**
+ * @file
+ *
+ * Definition of Drupal\Core\UrlMatcher.
+ */
+
+namespace Drupal\Core;
+
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+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 extends SymfonyUrlMatcher {
+
+  /**
+   * The request context for this matcher.
+   *
+   * @var RequestContext
+   */
+  protected $context;
+
+  /**
+   * Constructor.
+   *
+   * @param RequestContext  $context
+   *   The request context object.
+   */
+  public function __construct(RequestContext $context) {
+    $this->context = $context;
+  }
+
+  /**
+   * {@inheritDoc}
+   *
+   * @api
+   */
+  public function match($pathinfo) {
+
+    $this->allow = array();
+
+    // Symfony uses a prefixing / but we don't yet.
+    $dpathinfo = ltrim($pathinfo, '/');
+
+    // Do our fancy frontpage logic.
+    if (empty($dpathinfo)) {
+      $dpathinfo = variable_get('site_frontpage', 'user');
+      $pathinfo = '/' . $dpathinfo;
+    }
+
+    if ($router_item = $this->matchDrupalItem($dpathinfo)) {
+      $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);
+  }
+
+  protected function convertDrupalItem($router_item) {
+    $route = array(
+      '_controller' => $router_item['page_callback']
+    );
+
+    // Place argument defaults on the route.
+    // @TODO: For some reason drush test runs have a serialized page_arguments
+    // but HTTP requests are unserialized. Hack to get around this for now.
+    !is_array($router_item['page_arguments']) ? $page_arguments = unserialize($router_item['page_arguments']) : $page_arguments = $router_item['page_arguments'];
+    foreach ($page_arguments as $k => $v) {
+      $route[$k] = $v;
+    }
+    return $route;
+    return new Route($router_item['href'], $route);
+  }
+}
diff --git a/core/modules/overlay/overlay.module b/core/modules/overlay/overlay.module
index 58dcbab..ab577a3 100644
--- a/core/modules/overlay/overlay.module
+++ b/core/modules/overlay/overlay.module
@@ -5,6 +5,8 @@
  * Displays the Drupal administration interface in an overlay.
  */
 
+use Symfony\Component\HttpFoundation\Response;
+
 /**
  * Implements hook_help().
  */
@@ -19,7 +21,7 @@ function overlay_help($path, $arg) {
 }
 
 /**
- * Implements hook_menu().
+ * Implements hook_menu()
  */
 function overlay_menu() {
   $items['overlay-ajax/%'] = array(
@@ -975,5 +977,5 @@ function overlay_trigger_refresh() {
  * @see Drupal.overlay.refreshRegions()
  */
 function overlay_ajax_render_region($region) {
-  print overlay_render_region($region);
+  return new Response(overlay_render_region($region));
 }
diff --git a/core/modules/simpletest/tests/file.test b/core/modules/simpletest/tests/file.test
index c5eced1..00bda25 100644
--- a/core/modules/simpletest/tests/file.test
+++ b/core/modules/simpletest/tests/file.test
@@ -2423,7 +2423,7 @@ class FileDownloadTest extends FileTestCase {
 
     $this->checkUrl('public', '', $basename, $base_url . '/' . file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath() . '/' . $basename_encoded);
     $this->checkUrl('private', '', $basename, $base_url . '/system/files/' . $basename_encoded);
-    $this->checkUrl('private', '', $basename, $base_url . '/?q=system/files/' . $basename_encoded, '0');
+    $this->checkUrl('private', '', $basename, $base_url . '/index.php/system/files/' . $basename_encoded, '0');
   }
 
   /**
diff --git a/index.php b/index.php
index b91fb1e..76a34cd 100644
--- a/index.php
+++ b/index.php
@@ -11,11 +11,32 @@
  * See COPYRIGHT.txt and LICENSE.txt files in the "core" directory.
  */
 
+use Drupal\Core\DrupalKernel;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\HttpKernel\Controller\ControllerResolver;
+
 /**
  * Root directory of Drupal installation.
  */
 define('DRUPAL_ROOT', getcwd());
-
+// Bootstrap the lowest level of what we need.
 require_once DRUPAL_ROOT . '/core/includes/bootstrap.inc';
+drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
+
+// A request object from the HTTPFoundation to tell us about the request.
+$request = Request::createFromGlobals();
+
+// Set the global $request object.  This is a temporary measure to
+// keep legacy utility functions working.  It should be moved to a dependency
+// injection container at some point.
+request($request);
+
+
 drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
-menu_execute_active_handler();
+
+$dispatcher = new EventDispatcher();
+$resolver = new ControllerResolver();
+
+$kernel = new DrupalKernel($dispatcher, $resolver);
+$kernel->handle($request)->send();
