webserver: Move web server functionality into webservd

Moved libmicrohttpd from libwebserv to webservd daemon. Added the
D-Bus interface between the web server daemon and client library.

Updated privetd to use the new interface.

BUG=brillo:10
TEST=`FEATURES=test emerge-link libwebserv privetd`
CQ-DEPEND=CL:245780,CL:245118,CL:*195757

Change-Id: I26bfab64c6a0fd9460a47fd3fa9205c89abb943a
Reviewed-on: https://chromium-review.googlesource.com/245980
Reviewed-by: Alex Vakulenko <avakulenko@chromium.org>
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
Commit-Queue: Alex Vakulenko <avakulenko@chromium.org>
diff --git a/libwebserv/connection.cc b/libwebserv/connection.cc
deleted file mode 100644
index 4e1ae8f..0000000
--- a/libwebserv/connection.cc
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <libwebserv/connection.h>
-
-#include <algorithm>
-#include <vector>
-
-#include <base/bind.h>
-#include <base/location.h>
-#include <base/logging.h>
-#include <base/task_runner.h>
-#include <chromeos/http/http_request.h>
-#include <libwebserv/request.h>
-#include <libwebserv/request_handler_interface.h>
-#include <libwebserv/response.h>
-#include <libwebserv/server.h>
-#include <microhttpd.h>
-
-namespace libwebserv {
-
-// Helper class to provide static callback methods to microhttpd library,
-// with the ability to access private methods of Connection class.
-class ConnectionHelper {
- public:
-  static int PostDataIterator(void* cls,
-                              MHD_ValueKind kind,
-                              const char* key,
-                              const char* filename,
-                              const char* content_type,
-                              const char* transfer_encoding,
-                              const char* data,
-                              uint64_t off,
-                              size_t size) {
-    Connection* server_connection = reinterpret_cast<Connection*>(cls);
-    if (!server_connection->ProcessPostData(
-        key, filename, content_type, transfer_encoding, data, off, size)) {
-      return MHD_NO;
-    }
-    return MHD_YES;
-  }
-};
-
-// Helper class to provide static callback methods to microhttpd library,
-// with the ability to access private methods of Request class.
-class RequestHelper {
- public:
-  static int ValueCallback(void* cls,
-                           MHD_ValueKind kind,
-                           const char* key,
-                           const char* value) {
-    auto self = reinterpret_cast<Request*>(cls);
-    std::string data;
-    if (value)
-      data = value;
-    if (kind == MHD_HEADER_KIND) {
-      self->headers_.emplace(Request::GetCanonicalHeaderName(key), data);
-    } else if (kind == MHD_COOKIE_KIND) {
-      // TODO(avakulenko): add support for cookies...
-    } else if (kind == MHD_POSTDATA_KIND) {
-      self->post_data_.emplace(key, data);
-    } else if (kind == MHD_GET_ARGUMENT_KIND) {
-      self->get_data_.emplace(key, data);
-    }
-    return MHD_YES;
-  }
-};
-
-Connection::Connection(const scoped_refptr<base::TaskRunner>& task_runner,
-                       MHD_Connection* connection,
-                       RequestHandlerInterface* handler)
-    : task_runner_(task_runner),
-      raw_connection_(connection),
-      handler_(handler) {
-}
-
-Connection::~Connection() {
-  if (post_processor_)
-    MHD_destroy_post_processor(post_processor_);
-}
-
-scoped_refptr<Connection> Connection::Create(Server* server,
-                                             const std::string& url,
-                                             const std::string& method,
-                                             MHD_Connection* connection,
-                                             RequestHandlerInterface* handler) {
-  scoped_refptr<Connection> result(
-      new Connection(server->task_runner_, connection, handler));
-  VLOG(1) << "Incoming HTTP connection (" << result.get() << ")."
-          << " Method='" << method << "', URL='" << url << "'";
-  result->post_processor_ = MHD_create_post_processor(
-      connection, 1024, &ConnectionHelper::PostDataIterator, result.get());
-  result->request_ = Request::Create(url, method);
-  result->response_ = Response::Create(result);
-  return result;
-}
-
-bool Connection::BeginRequestData() {
-  MHD_get_connection_values(raw_connection_, MHD_HEADER_KIND,
-                            &RequestHelper::ValueCallback, request_.get());
-  MHD_get_connection_values(raw_connection_, MHD_COOKIE_KIND,
-                            &RequestHelper::ValueCallback, request_.get());
-  MHD_get_connection_values(raw_connection_, MHD_POSTDATA_KIND,
-                            &RequestHelper::ValueCallback, request_.get());
-  MHD_get_connection_values(raw_connection_, MHD_GET_ARGUMENT_KIND,
-                            &RequestHelper::ValueCallback, request_.get());
-  return true;
-}
-
-bool Connection::AddRequestData(const void* data, size_t size) {
-  if (!post_processor_)
-    return request_->AddRawRequestData(data, size);
-  return MHD_post_process(post_processor_,
-                          static_cast<const char*>(data), size) == MHD_YES;
-}
-
-void Connection::EndRequestData() {
-  if (state_ == State::kIdle) {
-    state_ = State::kRequestSent;
-    // libmicrohttpd calls handlers on its own thread.
-    // Redirect this to the main IO thread of the server.
-    task_runner_->PostTask(
-        FROM_HERE,
-        base::Bind(&RequestHandlerInterface::HandleRequest,
-                   base::Unretained(handler_), base::Passed(&request_),
-                   base::Passed(&response_)));
-  } else if (state_ == State::kResponseReceived) {
-    VLOG(1) << "Sending HTTP response for connection (" << this
-            << "): " << response_status_code_
-            << ", data size = " << response_data_.size();
-    MHD_Response* resp = MHD_create_response_from_buffer(
-        response_data_.size(), response_data_.data(), MHD_RESPMEM_PERSISTENT);
-    for (const auto& pair : response_headers_) {
-      MHD_add_response_header(resp, pair.first.c_str(), pair.second.c_str());
-    }
-    CHECK_EQ(MHD_YES,
-             MHD_queue_response(raw_connection_, response_status_code_, resp))
-        << "Failed to queue response";
-    MHD_destroy_response(resp);  // |resp| is ref-counted.
-    state_ = State::kDone;
-  }
-}
-
-bool Connection::ProcessPostData(const char* key,
-                                 const char* filename,
-                                 const char* content_type,
-                                 const char* transfer_encoding,
-                                 const char* data,
-                                 uint64_t off,
-                                 size_t size) {
-  if (off == 0)
-    return  request_->AddPostFieldData(key, filename, content_type,
-                                       transfer_encoding, data, size);
-  return request_->AppendPostFieldData(key, data, size);
-}
-
-}  // namespace libwebserv
diff --git a/libwebserv/connection.h b/libwebserv/connection.h
deleted file mode 100644
index 73bb816..0000000
--- a/libwebserv/connection.h
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef WEBSERVER_LIBWEBSERV_CONNECTION_H_
-#define WEBSERVER_LIBWEBSERV_CONNECTION_H_
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include <base/macros.h>
-#include <base/memory/ref_counted.h>
-#include <base/memory/scoped_ptr.h>
-#include <chromeos/errors/error.h>
-#include <libwebserv/export.h>
-
-struct MHD_Connection;
-struct MHD_PostProcessor;
-
-namespace base {
-class TaskRunner;
-}  // namespace base
-
-namespace libwebserv {
-
-class Request;
-class RequestHandlerInterface;
-class Response;
-class Server;
-
-// A wrapper class around low-level HTTP connection.
-class LIBWEBSERV_EXPORT Connection final : public base::RefCounted<Connection> {
- public:
-  ~Connection();
-
-  // Factory creator method. Creates an instance of the connection and
-  // initializes some complex data members. This is safer and easier to
-  // report possible failures than reply on just the constructor.
-  static scoped_refptr<Connection> Create(Server* server,
-                                          const std::string& url,
-                                          const std::string& method,
-                                          MHD_Connection* connection,
-                                          RequestHandlerInterface* handler);
-
- private:
-  LIBWEBSERV_PRIVATE Connection(
-      const scoped_refptr<base::TaskRunner>& task_runner,
-      MHD_Connection* connection,
-      RequestHandlerInterface* handler);
-
-  // Helper callback methods used by Server's ConnectionHandler to transfer
-  // request headers and data to the Connection's Request object.
-  LIBWEBSERV_PRIVATE bool BeginRequestData();
-  LIBWEBSERV_PRIVATE bool AddRequestData(const void* data, size_t size);
-  LIBWEBSERV_PRIVATE void EndRequestData();
-
-  // Callback for libmicrohttpd's PostProcessor.
-  LIBWEBSERV_PRIVATE bool ProcessPostData(const char* key,
-                                          const char* filename,
-                                          const char* content_type,
-                                          const char* transfer_encoding,
-                                          const char* data,
-                                          uint64_t off,
-                                          size_t size);
-
-  scoped_refptr<base::TaskRunner> task_runner_;
-  MHD_Connection* raw_connection_{nullptr};
-  RequestHandlerInterface* handler_{nullptr};
-  MHD_PostProcessor* post_processor_{nullptr};
-  scoped_ptr<Request> request_;
-  scoped_ptr<Response> response_;
-
-  enum class State { kIdle, kRequestSent, kResponseReceived, kDone };
-  State state_{State::kIdle};
-  int response_status_code_{0};
-  std::vector<uint8_t> response_data_;
-  std::multimap<std::string, std::string> response_headers_;
-
-  friend class ConnectionHelper;
-  friend class Request;
-  friend class Response;
-  friend class ServerHelper;
-  DISALLOW_COPY_AND_ASSIGN(Connection);
-};
-
-}  // namespace libwebserv
-
-#endif  // WEBSERVER_LIBWEBSERV_CONNECTION_H_
diff --git a/libwebserv/dbus_bindings/org.chromium.WebServer.RequestHandler.xml b/libwebserv/dbus_bindings/org.chromium.WebServer.RequestHandler.xml
new file mode 100644
index 0000000..5eab71d
--- /dev/null
+++ b/libwebserv/dbus_bindings/org.chromium.WebServer.RequestHandler.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node name="/org/chromium/WebServer/RequestHandler"
+      xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+  <interface name="org.chromium.WebServer.RequestHandler">
+    <!-- Methods -->
+    <method name="ProcessRequest">
+      <tp:docstring>
+        Sends a new HTTP request to the handler.
+        Parameters:
+        - request_info - request metadata. Due to limitation of base::Callback
+                       on the number of parameters, we have to collapse a couple
+                       of distinct parameters into a larger struct, containing:
+                       - (s) protocol_handler_id - ID of the protocol handler.
+                       - (s) request_handler_id - ID of the registered request
+                                                  handler.
+                       - (s) request_id - unique ID of this request within the
+                                          protocol handler.
+                       - (s) url - The request URL (e.g. "/path/object").
+                       - (s) method - Request method (e.g. "GET", "POST", ...).
+        - headers - Request headers (key-value pairs)
+        - params - an array of request parameters which could be either
+                   URL params (specified after "?" in the request URL), or
+                   form fields in a POST request. Elements have the following
+                   structure:
+                   - (b) true = form field, false = URL param
+                   - (s) field_name
+                   - (s) field_value
+        - files - Information about uploaded files.
+                  The data is an array of FileInfo structures containing the
+                  following fields:
+                  - (i) file_id
+                  - (s) field_name
+                  - (s) file_name
+                  - (s) content_type
+                  - (s) transfer_encoding
+                  The actual contents of the file is obtained by calling
+                  GetFileData() on the request object
+        - body - Raw unparsed request data. Could be empty for POST requests
+                 that have form data/uploaded files already parsed into
+                 form_fields/files parameters.
+      </tp:docstring>
+      <arg name="request_info" type="(sssss)" direction="in"/>
+      <arg name="headers" type="a(ss)" direction="in"/>
+      <arg name="params" type="a(bss)" direction="in"/>
+      <arg name="files" type="a(issss)" direction="in"/>
+      <arg name="body" type="ay" direction="in"/>
+      <annotation name="org.chromium.DBus.Method.Kind" value="normal"/>
+    </method>
+  </interface>
+</node>
diff --git a/libwebserv/protocol_handler.cc b/libwebserv/protocol_handler.cc
new file mode 100644
index 0000000..9a10acf
--- /dev/null
+++ b/libwebserv/protocol_handler.cc
@@ -0,0 +1,207 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webserver/libwebserv/protocol_handler.h"
+
+#include <tuple>
+
+#include <base/logging.h>
+#include <chromeos/map_utils.h>
+
+#include "libwebserv/org.chromium.WebServer.RequestHandler.h"
+#include "webservd/dbus-proxies.h"
+#include "webserver/libwebserv/request.h"
+#include "webserver/libwebserv/request_handler_callback.h"
+#include "webserver/libwebserv/response.h"
+#include "webserver/libwebserv/server.h"
+
+namespace libwebserv {
+
+namespace {
+
+// A dummy callback for async D-Bus errors.
+void IgnoreError(chromeos::Error* error) {}
+
+}  // anonymous namespace
+
+const char ProtocolHandler::kHttp[] = "http";
+const char ProtocolHandler::kHttps[] = "https";
+
+ProtocolHandler::ProtocolHandler(const std::string& id, Server* server)
+    : id_{id}, server_{server} {}
+
+ProtocolHandler::~ProtocolHandler() {
+  // Remove any existing handlers, so the web server knows that we don't
+  // need them anymore.
+
+  // We need to get a copy of the map keys since removing the handlers will
+  // modify the map in the middle of the loop and that's not a good thing.
+  auto handler_ids = chromeos::GetMapKeys(request_handlers_);
+  for (int handler_id : handler_ids) {
+    RemoveHandler(handler_id);
+  }
+}
+
+std::string ProtocolHandler::GetID() const {
+  return id_;
+}
+
+uint16_t ProtocolHandler::GetPort() const {
+  CHECK(IsConnected());
+  return proxy_->port();
+}
+
+std::string ProtocolHandler::GetProtocol() const {
+  CHECK(IsConnected());
+  return proxy_->protocol();
+}
+
+chromeos::Blob ProtocolHandler::GetCertificateFingerprint() const {
+  CHECK(IsConnected());
+  return proxy_->certificate_fingerprint();
+}
+
+int ProtocolHandler::AddHandler(
+    const std::string& url,
+    const std::string& method,
+    std::unique_ptr<RequestHandlerInterface> handler) {
+  request_handlers_.emplace(++last_handler_id_,
+                            HandlerMapEntry{url, method, std::string{},
+                                            std::move(handler)});
+  if (proxy_) {
+    proxy_->AddRequestHandlerAsync(
+        url,
+        method,
+        server_->service_name_,
+        base::Bind(&ProtocolHandler::AddHandlerSuccess,
+                   weak_ptr_factory_.GetWeakPtr(),
+                   last_handler_id_),
+        base::Bind(&ProtocolHandler::AddHandlerError,
+                   weak_ptr_factory_.GetWeakPtr(),
+                   last_handler_id_));
+  }
+  return last_handler_id_;
+}
+
+int ProtocolHandler::AddHandlerCallback(
+    const std::string& url,
+    const std::string& method,
+    const base::Callback<RequestHandlerInterface::HandlerSignature>&
+        handler_callback) {
+  std::unique_ptr<RequestHandlerInterface> handler{
+      new RequestHandlerCallback{handler_callback}};
+  return AddHandler(url, method, std::move(handler));
+}
+
+bool ProtocolHandler::RemoveHandler(int handler_id) {
+  auto p = request_handlers_.find(handler_id);
+  if (p == request_handlers_.end())
+    return false;
+
+  if (proxy_) {
+    proxy_->RemoveRequestHandlerAsync(
+        p->second.remote_handler_id,
+        base::Bind(&base::DoNothing),
+        base::Bind(&IgnoreError));
+  }
+
+  request_handlers_.erase(p);
+  return true;
+}
+
+void ProtocolHandler::Connect(
+    org::chromium::WebServer::ProtocolHandlerProxy* proxy) {
+  proxy_ = proxy;
+  for (const auto& pair : request_handlers_) {
+    proxy_->AddRequestHandlerAsync(
+        pair.second.url,
+        pair.second.method,
+        server_->service_name_,
+        base::Bind(&ProtocolHandler::AddHandlerSuccess,
+                   weak_ptr_factory_.GetWeakPtr(),
+                   pair.first),
+        base::Bind(&ProtocolHandler::AddHandlerError,
+                   weak_ptr_factory_.GetWeakPtr(),
+                   pair.first));
+  }
+}
+
+void ProtocolHandler::Disconnect() {
+  proxy_ = nullptr;
+  remote_handler_id_map_.clear();
+}
+
+void ProtocolHandler::AddHandlerSuccess(int handler_id,
+                                        const std::string& remote_handler_id) {
+  auto p = request_handlers_.find(handler_id);
+  CHECK(p != request_handlers_.end());
+  p->second.remote_handler_id = remote_handler_id;
+
+  remote_handler_id_map_.emplace(remote_handler_id, handler_id);
+}
+
+void ProtocolHandler::AddHandlerError(int handler_id, chromeos::Error* error) {
+  // Nothing to do at the moment.
+}
+
+bool ProtocolHandler::ProcessRequest(const std::string& remote_handler_id,
+                                     const std::string& request_id,
+                                     std::unique_ptr<Request> request,
+                                     chromeos::ErrorPtr* error) {
+  auto id_iter = remote_handler_id_map_.find(remote_handler_id);
+  if (id_iter == remote_handler_id_map_.end()) {
+    chromeos::Error::AddToPrintf(error, FROM_HERE,
+                                 chromeos::errors::dbus::kDomain,
+                                 DBUS_ERROR_FAILED,
+                                 "Unknown request handler '%s'",
+                                 remote_handler_id.c_str());
+    return false;
+  }
+  auto handler_iter = request_handlers_.find(id_iter->second);
+  if (handler_iter == request_handlers_.end()) {
+    chromeos::Error::AddToPrintf(error, FROM_HERE,
+                                 chromeos::errors::dbus::kDomain,
+                                 DBUS_ERROR_FAILED,
+                                 "Handler # %d is no longer available",
+                                 id_iter->second);
+    return false;
+  }
+  handler_iter->second.handler->HandleRequest(
+      scoped_ptr<Request>(request.release()),
+      scoped_ptr<Response>(new Response{this, request_id}));
+  return true;
+}
+
+void ProtocolHandler::CompleteRequest(
+    const std::string& request_id,
+    int status_code,
+    const std::multimap<std::string, std::string>& headers,
+    const std::vector<uint8_t>& data) {
+  if (!proxy_) {
+    LOG(WARNING) << "Completing a request after the handler proxy is removed";
+    return;
+  }
+
+  std::vector<std::tuple<std::string, std::string>> header_list;
+  header_list.reserve(headers.size());
+  for (const auto& pair : headers)
+    header_list.emplace_back(pair.first, pair.second);
+
+  proxy_->CompleteRequestAsync(request_id, status_code, header_list, data,
+                               base::Bind(&base::DoNothing),
+                               base::Bind([](chromeos::Error*) {}));
+}
+
+void ProtocolHandler::GetFileData(
+    const std::string& request_id,
+    int file_id,
+    const base::Callback<void(const std::vector<uint8_t>&)>& success_callback,
+    const base::Callback<void(chromeos::Error*)>& error_callback) {
+  CHECK(proxy_);
+  proxy_->GetRequestFileDataAsync(
+      request_id, file_id, success_callback, error_callback);
+}
+
+
+}  // namespace libwebserv
diff --git a/libwebserv/protocol_handler.h b/libwebserv/protocol_handler.h
new file mode 100644
index 0000000..9ba7d11
--- /dev/null
+++ b/libwebserv/protocol_handler.h
@@ -0,0 +1,187 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBSERVER_LIBWEBSERV_PROTOCOL_HANDLER_H_
+#define WEBSERVER_LIBWEBSERV_PROTOCOL_HANDLER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/callback_forward.h>
+#include <base/macros.h>
+#include <base/memory/weak_ptr.h>
+#include <chromeos/errors/error.h>
+#include <chromeos/secure_blob.h>
+
+#include <libwebserv/export.h>
+#include <libwebserv/request_handler_interface.h>
+
+namespace org {
+namespace chromium {
+namespace WebServer {
+
+class ProtocolHandlerProxy;
+
+}  // namespace WebServer
+}  // namespace chromium
+}  // namespace org
+
+namespace libwebserv {
+
+class Server;
+class Request;
+
+// Wrapper around a protocol handler (e.g. HTTP or HTTPs).
+// ProtocolHandler allows consumers to add request handlers on a given protocol.
+// When the ProtocolHandler is connected, allows users to read port and protocol
+// information.
+class LIBWEBSERV_EXPORT ProtocolHandler final {
+ public:
+  explicit ProtocolHandler(const std::string& id, Server* server);
+  ~ProtocolHandler();
+
+  // Returns true if the protocol handler object is connected to the web server
+  // daemon's proxy object and is capable of processing incoming requests.
+  bool IsConnected() const { return proxy_ != nullptr; }
+
+  // Handler's unique ID. This is generally a GUID string, except for the
+  // default HTTP and HTTPS handlers which have IDs of "http" and "https"
+  // respectively.
+  std::string GetID() const;
+
+  // Returns the port the handler is bound to.
+  // ATTENTION: The handler must be connected to the web server before this
+  // method can be called.
+  uint16_t GetPort() const;
+
+  // Returns the transport protocol that is served by this handler.
+  // Can be either "http" or "https".
+  // ATTENTION: The handler must be connected to the web server before this
+  // method can be called.
+  std::string GetProtocol() const;
+
+  // Returns a SHA-256 fingerprint of HTTPS certificate used. Returns an empty
+  // byte buffer if this handler does not serve the HTTPS protocol.
+  // ATTENTION: The handler must be connected to the web server before this
+  // method can be called.
+  chromeos::Blob GetCertificateFingerprint() const;
+
+  // Adds a request handler for given |url|. If the |url| ends with a '/', this
+  // makes the handler respond to any URL beneath this path.
+  // Note that it is not possible to add a specific handler just for the root
+  // path "/". Doing so means "respond to any URL".
+  // |method| is optional request method verb, such as "GET" or "POST".
+  // If |method| is empty, the handler responds to any request verb.
+  // If there are more than one handler for a given request, the most specific
+  // match is chosen. For example, if there are the following handlers provided:
+  //    - A["/foo/",  ""]
+  //    - B["/foo/bar", "GET"]
+  //    - C["/foo/bar", ""]
+  // Here is what handlers are called when making certain requests:
+  //    - GET("/foo/bar")   => B[]
+  //    - POST("/foo/bar")  => C[]
+  //    - PUT("/foo/bar")   => C[]
+  //    - GET("/foo/baz")   => A[]
+  //    - GET("/foo")       => 404 Not Found
+  // This functions returns a handler ID which can be used later to remove
+  // the handler.
+  //
+  // The handler registration information is stored inside ProtocolHandler and
+  // is used to register the handlers with the web server daemon when it becomes
+  // available. This also happens when the web server goes away and then comes
+  // back (e.g. restarted). So, there is no need to re-register the handlers
+  // once the web server process is restarted.
+  int AddHandler(const std::string& url,
+                 const std::string& method,
+                 std::unique_ptr<RequestHandlerInterface> handler);
+
+  // Similar to AddHandler() above but the handler is just a callback function.
+  int AddHandlerCallback(
+      const std::string& url,
+      const std::string& method,
+      const base::Callback<RequestHandlerInterface::HandlerSignature>&
+          handler_callback);
+
+  // Removes the handler with the specified |handler_id|.
+  // Returns false if the handler with the given ID is not found.
+  bool RemoveHandler(int handler_id);
+
+  static const char kHttp[];
+  static const char kHttps[];
+
+ private:
+  friend class FileInfo;
+  friend class Server;
+  friend class Response;
+
+  struct LIBWEBSERV_PRIVATE HandlerMapEntry {
+    std::string url;
+    std::string method;
+    std::string remote_handler_id;
+    std::unique_ptr<RequestHandlerInterface> handler;
+  };
+
+  // Called by the Server class when the D-Bus proxy object gets connected
+  // to the web server daemon.
+
+  LIBWEBSERV_PRIVATE void Connect(
+      org::chromium::WebServer::ProtocolHandlerProxy* proxy);
+  // Called by the Server class when the D-Bus proxy object gets disconnected
+  // from the web server daemon.
+  LIBWEBSERV_PRIVATE void Disconnect();
+
+  // Asynchronous callbacks to handle successful or failed request handler
+  // registration over D-Bus.
+  LIBWEBSERV_PRIVATE void AddHandlerSuccess(
+      int handler_id, const std::string& remote_handler_id);
+  LIBWEBSERV_PRIVATE void AddHandlerError(int handler_id,
+                                          chromeos::Error* error);
+
+  // Called by Server when an incoming request is dispatched.
+  LIBWEBSERV_PRIVATE bool ProcessRequest(const std::string& remote_handler_id,
+                                         const std::string& request_id,
+                                         std::unique_ptr<Request> request,
+                                         chromeos::ErrorPtr* error);
+
+  // Called by Response object to finish the request and send response data.
+  LIBWEBSERV_PRIVATE void CompleteRequest(
+      const std::string& request_id,
+      int status_code,
+      const std::multimap<std::string, std::string>& headers,
+      const std::vector<uint8_t>& data);
+
+  // Makes a call to the (remote) web server request handler over D-Bus to
+  // obtain the file content of uploaded file (identified by |file_id|) during
+  // request with |request_id|.
+  LIBWEBSERV_PRIVATE void GetFileData(
+    const std::string& request_id,
+    int file_id,
+    const base::Callback<void(const std::vector<uint8_t>&)>& success_callback,
+    const base::Callback<void(chromeos::Error*)>& error_callback);
+
+  // Protocol Handler unique ID.
+  std::string id_;
+  // Back reference to the server object.
+  Server* server_{nullptr};
+  // Handler data map. The key is the client-facing request handler ID returned
+  // by AddHandler() when registering the handler.
+  std::map<int, HandlerMapEntry> request_handlers_;
+  // Map of remote handler IDs (GUID strings) to client-facing request handler
+  // IDs (int) which are returned by AddHandler() and used as a key in
+  // |request_handlers_|.
+  std::map<std::string, int> remote_handler_id_map_;
+  // The counter to generate new handler IDs.
+  int last_handler_id_{0};
+  // Remove D-Bus proxy for the server protocol handler object.
+  org::chromium::WebServer::ProtocolHandlerProxy* proxy_{nullptr};
+
+  base::WeakPtrFactory<ProtocolHandler> weak_ptr_factory_{this};
+  DISALLOW_COPY_AND_ASSIGN(ProtocolHandler);
+};
+
+}  // namespace libwebserv
+
+#endif  // WEBSERVER_LIBWEBSERV_PROTOCOL_HANDLER_H_
diff --git a/libwebserv/request.cc b/libwebserv/request.cc
index 37ea1c1..b51aa94 100644
--- a/libwebserv/request.cc
+++ b/libwebserv/request.cc
@@ -4,86 +4,49 @@
 
 #include <libwebserv/request.h>
 
-#include <libwebserv/connection.h>
+#include <base/callback.h>
+#include <chromeos/http/http_utils.h>
+
+#include <libwebserv/protocol_handler.h>
 
 namespace libwebserv {
 
-FileInfo::FileInfo(const std::string& file_name,
+FileInfo::FileInfo(ProtocolHandler* handler,
+                   int file_id,
+                   const std::string& request_id,
+                   const std::string& file_name,
                    const std::string& content_type,
                    const std::string& transfer_encoding)
-    : file_name_(file_name),
+    : handler_{handler},
+      file_id_{file_id},
+      request_id_{request_id},
+      file_name_(file_name),
       content_type_(content_type),
       transfer_encoding_(transfer_encoding) {
 }
 
-const std::vector<uint8_t>& FileInfo::GetData() const {
-  return data_;
+void FileInfo::GetData(
+    const base::Callback<void(const std::vector<uint8_t>&)>& success_callback,
+    const base::Callback<void(chromeos::Error*)>& error_callback) {
+  handler_->GetFileData(request_id_,
+                        file_id_,
+                        success_callback,
+                        error_callback);
 }
 
-Request::Request(const std::string& url, const std::string& method)
-    : url_{url}, method_{method} {
+Request::Request(ProtocolHandler* handler,
+                 const std::string& url,
+                 const std::string& method)
+    : handler_{handler}, url_{url}, method_{method} {
 }
 
 Request::~Request() {
 }
 
-scoped_ptr<Request> Request::Create(const std::string& url,
-                                    const std::string& method) {
-  // Can't use make_shared here since Request::Request is private.
-  return scoped_ptr<Request>(new Request(url, method));
-}
-
 const std::vector<uint8_t>& Request::GetData() const {
   return raw_data_;
 }
 
-bool Request::AddRawRequestData(const void* data, size_t size) {
-  const uint8_t* byte_data_ = static_cast<const uint8_t*>(data);
-  raw_data_.insert(raw_data_.end(), byte_data_, byte_data_ + size);
-  return true;
-}
-
-bool Request::AddPostFieldData(const char* key,
-                               const char* filename,
-                               const char* content_type,
-                               const char* transfer_encoding,
-                               const char* data,
-                               size_t size) {
-  if (filename) {
-    std::unique_ptr<FileInfo> file_info{
-        new FileInfo{filename, content_type ? content_type : "",
-                     transfer_encoding ? transfer_encoding : ""}};
-    file_info->data_.assign(data, data + size);
-    file_info_.emplace(key, std::move(file_info));
-    last_posted_data_was_file_ = true;
-    return true;
-  }
-  std::string value{data, size};
-  post_data_.emplace(key, value);
-  last_posted_data_was_file_ = false;
-  return true;
-}
-
-bool Request::AppendPostFieldData(const char* key,
-                                  const char* data,
-                                  size_t size) {
-  if (last_posted_data_was_file_) {
-    auto file_pair = file_info_.equal_range(key);
-    if (file_pair.first == file_info_.end())
-      return false;
-    FileInfo* file_info = file_pair.second->second.get();
-    file_info->data_.insert(file_info->data_.end(), data, data + size);
-    return true;
-  }
-
-  auto pair = post_data_.equal_range(key);
-  if (pair.first == post_data_.end())
-    return false;
-  --pair.second;  // Get the last form field with this name/key.
-  pair.second->second.append(data, size);
-  return true;
-}
-
 std::vector<PairOfStrings> Request::GetFormData() const {
   auto data = GetFormDataGet();
   auto post_data = GetFormDataPost();
@@ -162,30 +125,19 @@
 
 std::vector<std::string> Request::GetHeader(const std::string& name) const {
   std::vector<std::string> data;
-  auto pair = headers_.equal_range(GetCanonicalHeaderName(name));
-  while (pair.first != pair.second) {
-    data.push_back(pair.first->second);
-    ++pair.first;
+  auto range =
+      headers_.equal_range(chromeos::http::GetCanonicalHeaderName(name));
+  while (range.first != range.second) {
+    data.push_back(range.first->second);
+    ++range.first;
   }
   return data;
 }
 
-std::string Request::GetCanonicalHeaderName(const std::string& name) {
-  std::string canonical_name = name;
-  bool word_begin = true;
-  for (char& c : canonical_name) {
-    if (c == '-') {
-      word_begin = true;
-    } else {
-      if (word_begin) {
-        c = toupper(c);
-      } else {
-        c = tolower(c);
-      }
-      word_begin = false;
-    }
-  }
-  return canonical_name;
+std::string Request::GetFirstHeader(const std::string& name) const {
+  auto p = headers_.find(chromeos::http::GetCanonicalHeaderName(name));
+  return (p != headers_.end()) ? p->second : std::string{};
 }
 
+
 }  // namespace libwebserv
diff --git a/libwebserv/request.h b/libwebserv/request.h
index 6899b3d..f75622b 100644
--- a/libwebserv/request.h
+++ b/libwebserv/request.h
@@ -11,36 +11,50 @@
 #include <utility>
 #include <vector>
 
+#include <base/callback_forward.h>
 #include <base/macros.h>
 #include <base/memory/ref_counted.h>
 #include <base/memory/scoped_ptr.h>
+#include <chromeos/errors/error.h>
 #include <libwebserv/export.h>
 
 struct MHD_Connection;
 
 namespace libwebserv {
 
-class Connection;
+class ProtocolHandler;
+
 using PairOfStrings = std::pair<std::string, std::string>;
 
+// This class represents the file information about a file uploaded via
+// POST request using multipart/form-data request.
 class LIBWEBSERV_EXPORT FileInfo final {
  public:
-  FileInfo(const std::string& file_name,
-           const std::string& content_type,
-           const std::string& transfer_encoding);
-
-  const std::vector<uint8_t>& GetData() const;
   const std::string& GetFileName() const { return file_name_; }
   const std::string& GetContentType() const { return content_type_; }
   const std::string& GetTransferEncoding() const { return transfer_encoding_; }
+  void GetData(
+      const base::Callback<void(const std::vector<uint8_t>&)>& success_callback,
+      const base::Callback<void(chromeos::Error*)>& error_callback);
 
  private:
+  friend class Server;
+
+  LIBWEBSERV_PRIVATE FileInfo(ProtocolHandler* handler,
+                              int file_id,
+                              const std::string& request_id,
+                              const std::string& file_name,
+                              const std::string& content_type,
+                              const std::string& transfer_encoding);
+
+  ProtocolHandler* handler_{nullptr};
+  int file_id_{0};
+  std::string request_id_;
   std::string file_name_;
   std::string content_type_;
   std::string transfer_encoding_;
   std::vector<uint8_t> data_;
 
-  friend class Request;
   DISALLOW_COPY_AND_ASSIGN(FileInfo);
 };
 
@@ -49,10 +63,6 @@
  public:
   ~Request();
 
-  // Factory constructor method.
-  static scoped_ptr<Request> Create(const std::string& url,
-                                    const std::string& method);
-
   // Gets the request body data stream. Note that the stream is available
   // only for requests that provided data and if this data is not already
   // pre-parsed by the server (e.g. "application/x-www-form-urlencoded" and
@@ -100,32 +110,22 @@
   // Returns a list of key-value pairs for all the request headers.
   std::vector<PairOfStrings> GetHeaders() const;
 
-  // Returns the value(s) of a request head of given |name|.
+  // Returns the value(s) of a request header of given |name|.
   std::vector<std::string> GetHeader(const std::string& name) const;
 
+  // Returns the value of a request header of given |name|. If there are more
+  // than one header with this name, the value of the first header is returned.
+  // An empty string is returned if the header does not exist in the request.
+  std::string GetFirstHeader(const std::string& name) const;
+
  private:
-  LIBWEBSERV_PRIVATE Request(const std::string& url, const std::string& method);
+  friend class Server;
 
-  // Helper methods for processing request data coming from the raw HTTP
-  // connection.
-  // These methods parse the request headers and data so they can be accessed
-  // by request handlers later.
-  LIBWEBSERV_PRIVATE bool AddRawRequestData(const void* data, size_t size);
-  LIBWEBSERV_PRIVATE bool AddPostFieldData(const char* key,
-                                           const char* filename,
-                                           const char* content_type,
-                                           const char* transfer_encoding,
-                                           const char* data,
-                                           size_t size);
-  LIBWEBSERV_PRIVATE bool AppendPostFieldData(const char* key,
-                                              const char* data,
-                                              size_t size);
-  // Converts a request header name to canonical form (lowercase with uppercase
-  // first letter and each letter after a hyphen ('-')).
-  // "content-TYPE" will be converted to "Content-Type".
-  LIBWEBSERV_PRIVATE static std::string GetCanonicalHeaderName(
-      const std::string& name);
+  LIBWEBSERV_PRIVATE Request(ProtocolHandler* handler,
+                             const std::string& url,
+                             const std::string& method);
 
+  ProtocolHandler* handler_{nullptr};
   std::string url_;
   std::string method_;
   std::vector<uint8_t> raw_data_;
@@ -136,8 +136,6 @@
   std::multimap<std::string, std::unique_ptr<FileInfo>> file_info_;
   std::multimap<std::string, std::string> headers_;
 
-  friend class Connection;
-  friend class RequestHelper;
   DISALLOW_COPY_AND_ASSIGN(Request);
 };
 
diff --git a/libwebserv/response.cc b/libwebserv/response.cc
index c54747f..fc46e87 100644
--- a/libwebserv/response.cc
+++ b/libwebserv/response.cc
@@ -12,13 +12,12 @@
 #include <chromeos/http/http_request.h>
 #include <chromeos/mime_utils.h>
 #include <chromeos/strings/string_utils.h>
-#include <libwebserv/connection.h>
-#include <microhttpd.h>
+#include <libwebserv/protocol_handler.h>
 
 namespace libwebserv {
 
-Response::Response(const scoped_refptr<Connection>& connection)
-    : connection_(connection) {
+Response::Response(ProtocolHandler* handler, const std::string& request_id)
+    : handler_{handler}, request_id_{request_id} {
 }
 
 Response::~Response() {
@@ -28,11 +27,6 @@
   }
 }
 
-scoped_ptr<Response> Response::Create(
-    const scoped_refptr<Connection>& connection) {
-  return scoped_ptr<Response>(new Response(connection));
-}
-
 void Response::AddHeader(const std::string& header_name,
                          const std::string& value) {
   headers_.emplace(header_name, value);
@@ -99,12 +93,7 @@
 void Response::SendResponse() {
   CHECK(!reply_sent_) << "Response already sent";
   reply_sent_ = true;
-  CHECK(connection_->state_ == Connection::State::kRequestSent)
-      << "Unexpected connection state";
-  connection_->response_status_code_ = status_code_;
-  connection_->response_data_ = std::move(data_);
-  connection_->response_headers_ = std::move(headers_);
-  connection_->state_ = Connection::State::kResponseReceived;
+  handler_->CompleteRequest(request_id_, status_code_, headers_, data_);
 }
 
 }  // namespace libwebserv
diff --git a/libwebserv/response.h b/libwebserv/response.h
index 05465e3..616264d 100644
--- a/libwebserv/response.h
+++ b/libwebserv/response.h
@@ -12,31 +12,22 @@
 #include <vector>
 
 #include <base/macros.h>
-#include <base/memory/ref_counted.h>
-#include <base/memory/scoped_ptr.h>
 #include <libwebserv/export.h>
 
 namespace base {
 class Value;
 }  // namespace base
 
-struct MHD_Connection;
-
 namespace libwebserv {
 
-class Connection;
+class ProtocolHandler;
 
 // Response class is a proxy for HTTP response used by the request handler
 // to provide response HTTP headers and data.
-class LIBWEBSERV_EXPORT Response
-    : public std::enable_shared_from_this<Response> {
+class LIBWEBSERV_EXPORT Response final {
  public:
   ~Response();
 
-  // Factory constructor method.
-  static scoped_ptr<Response> Create(
-      const scoped_refptr<Connection>& connection);
-
   // Adds a single HTTP response header to the response.
   void AddHeader(const std::string& header_name, const std::string& value);
 
@@ -76,19 +67,20 @@
   void ReplyWithErrorNotFound();
 
  private:
-  LIBWEBSERV_PRIVATE explicit Response(
-      const scoped_refptr<Connection>& connection);
+  friend class ProtocolHandler;
+
+  LIBWEBSERV_PRIVATE Response(ProtocolHandler* handler,
+                              const std::string& request_id);
 
   LIBWEBSERV_PRIVATE void SendResponse();
 
-  scoped_refptr<Connection> connection_;
+  ProtocolHandler* handler_{nullptr};
+  std::string request_id_;
   int status_code_{0};
   std::vector<uint8_t> data_;
   std::multimap<std::string, std::string> headers_;
   bool reply_sent_{false};
 
-  friend class Connection;
-  friend class ResponseHelper;
   DISALLOW_COPY_AND_ASSIGN(Response);
 };
 
diff --git a/libwebserv/server.cc b/libwebserv/server.cc
index 196f178..2bf7c7d 100644
--- a/libwebserv/server.cc
+++ b/libwebserv/server.cc
@@ -4,230 +4,201 @@
 
 #include <libwebserv/server.h>
 
-#include <limits>
+#include <tuple>
 #include <vector>
 
-#include <base/logging.h>
-#include <base/message_loop/message_loop_proxy.h>
-#include <libwebserv/connection.h>
+#include <libwebserv/protocol_handler.h>
 #include <libwebserv/request.h>
-#include <libwebserv/request_handler_callback.h>
-#include <libwebserv/request_handler_interface.h>
-#include <libwebserv/response.h>
-#include <microhttpd.h>
+
+#include "libwebserv/org.chromium.WebServer.RequestHandler.h"
+#include "webservd/dbus-proxies.h"
 
 namespace libwebserv {
 
-namespace {
-// Simple static request handler that just returns "404 Not Found" error.
-class PageNotFoundHandler : public RequestHandlerInterface {
+class Server::RequestHandler final
+    : public org::chromium::WebServer::RequestHandlerInterface {
  public:
-  void HandleRequest(scoped_ptr<Request> request,
-                     scoped_ptr<Response> response) override {
-    response->ReplyWithErrorNotFound();
-  }
+  explicit RequestHandler(Server* server) : server_{server} {}
+  bool ProcessRequest(
+      chromeos::ErrorPtr* error,
+      const std::tuple<std::string, std::string, std::string, std::string,
+                       std::string>& in_request_info,
+      const std::vector<std::tuple<std::string, std::string>>& in_headers,
+      const std::vector<std::tuple<bool, std::string, std::string>>& in_params,
+      const std::vector<std::tuple<int32_t, std::string, std::string,
+                                   std::string, std::string>>& in_files,
+      const std::vector<uint8_t>& in_body) override;
+
+ private:
+  Server* server_{nullptr};
+  DISALLOW_COPY_AND_ASSIGN(RequestHandler);
 };
 
-}  // anonymous namespace
-
-// Helper class to provide static callback methods to microhttpd library,
-// with the ability to access private methods of Server class.
-class ServerHelper {
- public:
-  static int ConnectionHandler(void *cls,
-                               MHD_Connection* connection,
-                               const char* url,
-                               const char* method,
-                               const char* version,
-                               const char* upload_data,
-                               size_t* upload_data_size,
-                               void** con_cls) {
-    Server* server = reinterpret_cast<Server*>(cls);
-    if (nullptr == *con_cls) {
-      RequestHandlerInterface* handler = server->FindHandler(url, method);
-      if (!handler)
-        return MHD_NO;
-
-      auto server_connection =
-          Connection::Create(server, url, method, connection, handler);
-      if (!server_connection || !server_connection->BeginRequestData())
-        return MHD_NO;
-
-      *con_cls = server_connection.get();
-      server_connection->AddRef();
-    } else {
-      Connection* connection = reinterpret_cast<Connection*>(*con_cls);
-      if (*upload_data_size) {
-        if (!connection->AddRequestData(upload_data, *upload_data_size))
-          return MHD_NO;
-        *upload_data_size = 0;
-      } else {
-        connection->EndRequestData();
-      }
-    }
-    return MHD_YES;
+bool Server::RequestHandler::ProcessRequest(
+    chromeos::ErrorPtr* error,
+    const std::tuple<std::string, std::string, std::string, std::string,
+                     std::string>& in_request_info,
+    const std::vector<std::tuple<std::string, std::string>>& in_headers,
+    const std::vector<std::tuple<bool, std::string, std::string>>& in_params,
+    const std::vector<std::tuple<int32_t, std::string, std::string,
+                                 std::string, std::string>>& in_files,
+    const std::vector<uint8_t>& in_body) {
+  std::string protocol_handler_id = std::get<0>(in_request_info);
+  std::string request_handler_id = std::get<1>(in_request_info);
+  std::string request_id = std::get<2>(in_request_info);
+  std::string url = std::get<3>(in_request_info);
+  std::string method = std::get<4>(in_request_info);
+  ProtocolHandler* protocol_handler =
+      server_->GetProtocolHandler(protocol_handler_id);
+  if (!protocol_handler) {
+    chromeos::Error::AddToPrintf(error, FROM_HERE,
+                                 chromeos::errors::dbus::kDomain,
+                                 DBUS_ERROR_FAILED,
+                                 "Unknown protocol handler '%s'",
+                                 protocol_handler_id.c_str());
+    return false;
+  }
+  std::unique_ptr<Request> request{new Request{protocol_handler, url, method}};
+  // Convert request data into format required by the Request object.
+  for (const auto& tuple : in_params) {
+    if (std::get<0>(tuple))
+      request->post_data_.emplace(std::get<1>(tuple), std::get<2>(tuple));
+    else
+      request->get_data_.emplace(std::get<1>(tuple), std::get<2>(tuple));
   }
 
-  static void RequestCompleted(void* cls,
-                               MHD_Connection* connection,
-                               void** con_cls,
-                               MHD_RequestTerminationCode toe) {
-    reinterpret_cast<Connection*>(*con_cls)->Release();
-    *con_cls = nullptr;
-  }
-};
+  for (const auto& tuple : in_headers)
+    request->headers_.emplace(std::get<0>(tuple), std::get<1>(tuple));
 
-Server::Server() {}
+  for (const auto& tuple : in_files) {
+    request->file_info_.emplace(
+        std::get<1>(tuple),  // field_name
+        std::unique_ptr<FileInfo>{new FileInfo{
+            protocol_handler,
+            std::get<0>(tuple),     // file_id
+            request_id,
+            std::get<2>(tuple),     // file_name
+            std::get<3>(tuple),     // content_type
+            std::get<4>(tuple)}});  // transfer_encoding
+  }
+
+  request->raw_data_ = in_body;
+
+  return protocol_handler->ProcessRequest(request_handler_id,
+                                          request_id,
+                                          std::move(request),
+                                          error);
+}
+
+Server::Server()
+    : request_handler_{new RequestHandler{this}},
+      dbus_adaptor_{new org::chromium::WebServer::RequestHandlerAdaptor{
+          request_handler_.get()}} {}
 
 Server::~Server() {
-  Stop();
 }
 
-bool Server::Start(uint16_t port) {
-  return StartWithTLS(port, chromeos::SecureBlob{}, chromeos::Blob{});
+void Server::Connect(
+    const scoped_refptr<dbus::Bus>& bus,
+    const std::string& service_name,
+    const chromeos::dbus_utils::AsyncEventSequencer::CompletionAction& cb,
+    const base::Closure& on_server_online,
+    const base::Closure& on_server_offline) {
+  service_name_ = service_name;
+  dbus_object_.reset(new chromeos::dbus_utils::DBusObject{
+      nullptr, bus, dbus_adaptor_->GetObjectPath()});
+  dbus_adaptor_->RegisterWithDBusObject(dbus_object_.get());
+  dbus_object_->RegisterAsync(cb);
+  on_server_online_ = on_server_online;
+  on_server_offline_ = on_server_offline;
+  AddProtocolHandler(std::unique_ptr<ProtocolHandler>{
+      new ProtocolHandler{ProtocolHandler::kHttp, this}});
+  AddProtocolHandler(std::unique_ptr<ProtocolHandler>{
+    new ProtocolHandler{ProtocolHandler::kHttps, this}});
+  object_manager_.reset(new org::chromium::WebServer::ObjectManagerProxy{bus});
+  object_manager_->SetServerAddedCallback(
+      base::Bind(&Server::Online, base::Unretained(this)));
+  object_manager_->SetServerRemovedCallback(
+      base::Bind(&Server::Offline, base::Unretained(this)));
+  object_manager_->SetProtocolHandlerAddedCallback(
+      base::Bind(&Server::ProtocolHandlerAdded, base::Unretained(this)));
+  object_manager_->SetProtocolHandlerRemovedCallback(
+      base::Bind(&Server::ProtocolHandlerRemoved, base::Unretained(this)));
 }
 
-bool Server::StartWithTLS(uint16_t port,
-                          const chromeos::SecureBlob& private_key,
-                          const chromeos::Blob& certificate) {
-  if (server_) {
-    LOG(ERROR) << "Web server is already running.";
-    return false;
+void Server::Disconnect() {
+  object_manager_.reset();
+  on_server_offline_.Reset();
+  on_server_online_.Reset();
+  dbus_object_.reset();
+  protocol_handlers_.clear();
+}
+
+void Server::Online(org::chromium::WebServer::ServerProxy* server) {
+  proxy_ = server;
+  if (!on_server_online_.is_null())
+    on_server_online_.Run();
+}
+
+void Server::Offline(const dbus::ObjectPath& object_path) {
+  if (!on_server_offline_.is_null())
+    on_server_offline_.Run();
+  proxy_ = nullptr;
+}
+
+void Server::ProtocolHandlerAdded(
+    org::chromium::WebServer::ProtocolHandlerProxy* handler) {
+  protocol_handler_id_map_.emplace(handler->GetObjectPath(), handler->id());
+  ProtocolHandler* registered_handler = GetProtocolHandler(handler->id());
+  if (registered_handler) {
+    registered_handler->Connect(handler);
+    if (!on_protocol_handler_connected_.is_null())
+      on_protocol_handler_connected_.Run(registered_handler);
+  }
+}
+
+void Server::ProtocolHandlerRemoved(const dbus::ObjectPath& object_path) {
+  auto p = protocol_handler_id_map_.find(object_path);
+  if (p == protocol_handler_id_map_.end())
+    return;
+
+  ProtocolHandler* registered_handler = GetProtocolHandler(p->second);
+  if (registered_handler) {
+    if (!on_protocol_handler_disconnected_.is_null())
+      on_protocol_handler_disconnected_.Run(registered_handler);
+    registered_handler->Disconnect();
   }
 
-  // Either both keys and certificate must be specified or both muse be omitted.
-  CHECK_EQ(private_key.empty(), certificate.empty());
-
-  const bool use_tls = !private_key.empty();
-
-  task_runner_ = base::MessageLoopProxy::current();
-
-  LOG(INFO) << "Starting " << (use_tls ? "HTTPS" : "HTTP")
-            << " Server on port: " << port;
-  auto callback_addr =
-      reinterpret_cast<intptr_t>(&ServerHelper::RequestCompleted);
-  uint32_t flags = MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG;
-
-  std::vector<MHD_OptionItem> options{
-    {MHD_OPTION_CONNECTION_LIMIT, 10, nullptr},
-    {MHD_OPTION_CONNECTION_TIMEOUT, 60, nullptr},
-    {MHD_OPTION_NOTIFY_COMPLETED, callback_addr, nullptr},
-  };
-
-  // libmicrohttpd expects both the key and certificate to be a zero-terminated
-  // strings. Make sure they are terminated properly.
-  chromeos::SecureBlob private_key_copy = private_key;
-  chromeos::Blob certificate_copy = certificate;
-  if (use_tls) {
-    flags |= MHD_USE_SSL;
-    private_key_copy.push_back(0);
-    certificate_copy.push_back(0);
-    options.push_back(
-        MHD_OptionItem{MHD_OPTION_HTTPS_MEM_KEY, 0, private_key_copy.data()});
-    options.push_back(
-        MHD_OptionItem{MHD_OPTION_HTTPS_MEM_CERT, 0, certificate_copy.data()});
-  }
-
-  options.push_back(MHD_OptionItem{MHD_OPTION_END, 0, nullptr});
-
-  server_ = MHD_start_daemon(flags, port, nullptr, nullptr,
-                             &ServerHelper::ConnectionHandler, this,
-                             MHD_OPTION_ARRAY, options.data(), MHD_OPTION_END);
-  if (!server_) {
-    LOG(ERROR) << "Failed to start the web server on port " << port;
-    return false;
-  }
-  LOG(INFO) << "Server started";
-  return true;
+  protocol_handler_id_map_.erase(p);
 }
 
-bool Server::Stop() {
-  if (server_) {
-    LOG(INFO) << "Shutting down the web server...";
-    MHD_stop_daemon(server_);
-    server_ = nullptr;
-    LOG(INFO) << "Server shutdown complete";
-  }
-  return true;
+ProtocolHandler* Server::GetProtocolHandler(const std::string& id) const {
+  auto p = protocol_handlers_.find(id);
+  return (p != protocol_handlers_.end()) ? p->second.get() : nullptr;
 }
 
-int Server::AddHandler(const base::StringPiece& url,
-                       const base::StringPiece& method,
-                       std::unique_ptr<RequestHandlerInterface> handler) {
-  request_handlers_.emplace(++last_handler_id_,
-                            HandlerMapEntry{url.as_string(),
-                                            method.as_string(),
-                                            std::move(handler)});
-  return last_handler_id_;
+ProtocolHandler* Server::GetDefaultHttpHandler() const {
+  return GetProtocolHandler(ProtocolHandler::kHttp);
 }
 
-int Server::AddHandlerCallback(
-    const base::StringPiece& url,
-    const base::StringPiece& method,
-    const base::Callback<RequestHandlerInterface::HandlerSignature>&
-        handler_callback) {
-  std::unique_ptr<RequestHandlerInterface> handler{
-      new RequestHandlerCallback{handler_callback}};
-  return AddHandler(url, method, std::move(handler));
+ProtocolHandler* Server::GetDefaultHttpsHandler() const {
+  return GetProtocolHandler(ProtocolHandler::kHttps);
 }
 
-bool Server::RemoveHandler(int handler_id) {
-  return (request_handlers_.erase(handler_id) > 0);
+void Server::OnProtocolHandlerConnected(
+    const base::Callback<void(ProtocolHandler*)>& callback) {
+  on_protocol_handler_connected_ = callback;
 }
 
-int Server::GetHandlerId(const base::StringPiece& url,
-                         const base::StringPiece& method) const {
-  for (const auto& pair : request_handlers_) {
-    if (pair.second.url == url && pair.second.method == method)
-      return pair.first;
-  }
-  return 0;
+void Server::OnProtocolHandlerDisconnected(
+    const base::Callback<void(ProtocolHandler*)>& callback) {
+  on_protocol_handler_disconnected_ = callback;
 }
 
-RequestHandlerInterface* Server::FindHandler(
-    const base::StringPiece& url,
-    const base::StringPiece& method) const {
-  static PageNotFoundHandler page_not_found_handler;
-
-  size_t score = std::numeric_limits<size_t>::max();
-  RequestHandlerInterface* handler = nullptr;
-  for (const auto& pair : request_handlers_) {
-    std::string handler_url = pair.second.url;
-    bool url_match = (handler_url == url);
-    bool method_match = (pair.second.method == method);
-
-    // Try exact match first. If everything matches, we have our handler.
-    if (url_match && method_match)
-      return pair.second.handler.get();
-
-    // Calculate the current handler's similarity score. The lower the score
-    // the better the match is...
-    size_t current_score = 0;
-    if (!url_match && !handler_url.empty() && handler_url.back() == '/') {
-      if (url.starts_with(handler_url)) {
-        url_match = true;
-        // Use the difference in URL length as URL match quality proxy.
-        // The longer URL, the more specific (better) match is.
-        // Multiply by 2 to allow for extra score point for matching the method.
-        current_score = (url.size() - handler_url.size()) * 2;
-      }
-    }
-
-    if (!method_match && pair.second.method.empty()) {
-      // If the handler didn't specify the method it handles, this means
-      // it doesn't care. However this isn't the exact match, so bump
-      // the score up one point.
-      method_match = true;
-      ++current_score;
-    }
-
-    if (url_match && method_match && current_score < score) {
-      score = current_score;
-      handler = pair.second.handler.get();
-    }
-  }
-
-  return handler ? handler : &page_not_found_handler;
+void Server::AddProtocolHandler(std::unique_ptr<ProtocolHandler> handler) {
+  // Make sure a handler with this ID isn't already registered.
+  CHECK(protocol_handlers_.find(handler->GetID()) == protocol_handlers_.end());
+  protocol_handlers_.emplace(handler->GetID(), std::move(handler));
 }
 
 }  // namespace libwebserv
diff --git a/libwebserv/server.h b/libwebserv/server.h
index 6221115..ba133df 100644
--- a/libwebserv/server.h
+++ b/libwebserv/server.h
@@ -9,110 +9,135 @@
 #include <memory>
 #include <string>
 
-#include <base/callback_forward.h>
 #include <base/macros.h>
-#include <base/memory/ref_counted.h>
-#include <base/strings/string_piece.h>
-#include <chromeos/secure_blob.h>
+#include <dbus/bus.h>
+#include <chromeos/dbus/async_event_sequencer.h>
+#include <chromeos/dbus/dbus_object.h>
 #include <libwebserv/export.h>
 #include <libwebserv/request_handler_interface.h>
 
-struct MHD_Daemon;
+namespace org {
+namespace chromium {
+namespace WebServer {
 
-namespace base {
-class TaskRunner;
-}  // namespace base
+class ObjectManagerProxy;
+class ProtocolHandlerProxy;
+class RequestHandlerAdaptor;
+class ServerProxy;
+
+}  // namespace WebServer
+}  // namespace chromium
+}  // namespace org
 
 namespace libwebserv {
 
 // Top-level wrapper class around HTTP server and provides an interface to
-// the web server. It allows the users to start the server and
-// register request handlers.
+// the web server.
 class LIBWEBSERV_EXPORT Server final {
  public:
   Server();
   ~Server();
 
-  // Starts the server and makes it listen to HTTP requests on the given port.
-  //
-  // Note that a valid message loop must exist before calling Start.
-  bool Start(uint16_t port);
+  // Establish a connection to the system webserver.
+  // |service_name| is the well known D-Bus name of the client's process, used
+  // to expose a callback D-Bus object the web server calls back with incoming
+  // requests.
+  // |on_server_online| and |on_server_offline| will notify the caller when the
+  // server comes up and down.
+  // Note that we can Connect() even before the webserver attaches to D-Bus,
+  // and appropriate state will be built up when the webserver appears on D-Bus.
+  void Connect(
+      const scoped_refptr<dbus::Bus>& bus,
+      const std::string& service_name,
+      const chromeos::dbus_utils::AsyncEventSequencer::CompletionAction& cb,
+      const base::Closure& on_server_online,
+      const base::Closure& on_server_offline);
 
-  // Starts the server and makes it listen to HTTPS requests on the given port.
-  //
-  // Note that a valid message loop must exist before calling StartWithTLS.
-  bool StartWithTLS(uint16_t port,
-                    const chromeos::SecureBlob& private_key,
-                    const chromeos::Blob& certificate);
+  // Disconnects from the web server and removes the library interface from
+  // D-Bus.
+  void Disconnect();
 
-  // Stops the server.
-  bool Stop();
+  // A helper method that returns the default handler for "http".
+  ProtocolHandler* GetDefaultHttpHandler() const;
 
-  // Adds a request handler for given |url|. If the |url| ends with a '/', this
-  // makes the handler respond to any URL beneath this path.
-  // Note that it is not possible to add a specific handler just for the root
-  // path "/". Doing so means "respond to any URL".
-  // |method| is optional request method verb, such as "GET" or "POST".
-  // If |method| is empty, the handler responds to any request verb.
-  // If there are more than one handler for a given request, the most specific
-  // match is chosen. For example, if there are the following handlers provided:
-  //    - A["/foo/",  ""]
-  //    - B["/foo/bar", "GET"]
-  //    - C["/foo/bar", ""]
-  // Here is what handlers are called when making certain requests:
-  //    - GET("/foo/bar")   => B[]
-  //    - POST("/foo/bar")  => C[]
-  //    - PUT("/foo/bar")   => C[]
-  //    - GET("/foo/baz")   => A[]
-  //    - GET("/foo")       => 404 Not Found
-  // This functions returns a handler ID which can be used later to remove
-  // the handler.
-  int AddHandler(const base::StringPiece& url,
-                 const base::StringPiece& method,
-                 std::unique_ptr<RequestHandlerInterface> handler);
+  // A helper method that returns the default handler for "https".
+  ProtocolHandler* GetDefaultHttpsHandler() const;
 
-  // Similar to AddHandler() above but the handler is just a callback function.
-  int AddHandlerCallback(
-      const base::StringPiece& url,
-      const base::StringPiece& method,
-      const base::Callback<RequestHandlerInterface::HandlerSignature>&
-          handler_callback);
+  // Returns true if the web server daemon is connected to DBus and our
+  // connection to it has been established.
+  bool IsConnected() const { return proxy_ != nullptr; }
 
-  // Removes the handler with the specified |handler_id|.
-  // Returns false if the handler with the given ID is not found.
-  bool RemoveHandler(int handler_id);
+  // Set a user-callback to be invoked when a protocol handler is connect to the
+  // server daemon.  Multiple calls to this method will overwrite previously set
+  // callbacks.
+  void OnProtocolHandlerConnected(
+      const base::Callback<void(ProtocolHandler*)>& callback);
 
-  // Finds the handler ID given the exact match criteria. Note that using
-  // this function could cause unexpected side effects if there are more than
-  // one handler registered for given URL/Method parameters.
-  // It is better to remember the handler ID from AddHandler() method and use
-  // that ID to remove the handler, instead of looking the handler up using
-  // URL/Method.
-  int GetHandlerId(const base::StringPiece& url,
-                   const base::StringPiece& method) const;
-
-  // Finds a handler for given URL/Method. This method does the criteria
-  // matching and not exact match performed by GetHandlerId. This is the method
-  // used to look up the handler for incoming HTTP requests.
-  RequestHandlerInterface* FindHandler(
-      const base::StringPiece& url,
-      const base::StringPiece& method) const;
+  // Set a user-callback to be invoked when a protocol handler is disconnected
+  // from the server daemon (e.g. on shutdown).  Multiple calls to this method
+  // will overwrite previously set callbacks.
+  void OnProtocolHandlerDisconnected(
+      const base::Callback<void(ProtocolHandler*)>& callback);
 
  private:
-  MHD_Daemon* server_ = nullptr;
-  scoped_refptr<base::TaskRunner> task_runner_;
+  friend class ProtocolHandler;
+  class RequestHandler;
 
-  struct LIBWEBSERV_PRIVATE HandlerMapEntry {
-    std::string url;
-    std::string method;
-    std::unique_ptr<RequestHandlerInterface> handler;
-  };
+  // Returns an existing protocol handler by ID.  See documentation in
+  // ProtocolHandler about IDs and how they work with webservd.
+  LIBWEBSERV_PRIVATE ProtocolHandler* GetProtocolHandler(
+      const std::string& id) const;
 
-  std::map<int, HandlerMapEntry> request_handlers_;
-  int last_handler_id_{0};
+  // Handler invoked when a connection is established to web server daemon.
+  LIBWEBSERV_PRIVATE void Online(org::chromium::WebServer::ServerProxy* server);
 
-  friend class ServerHelper;
-  friend class Connection;
+  // Handler invoked when the web server daemon connection is dropped.
+  LIBWEBSERV_PRIVATE void Offline(const dbus::ObjectPath& object_path);
+
+  // Handler invoked when a new protocol handler D-Bus proxy object becomes
+  // available.
+  LIBWEBSERV_PRIVATE void ProtocolHandlerAdded(
+      org::chromium::WebServer::ProtocolHandlerProxy* handler);
+
+  // Handler invoked when a protocol handler D-Bus proxy object disappears.
+  LIBWEBSERV_PRIVATE void ProtocolHandlerRemoved(
+      const dbus::ObjectPath& object_path);
+
+  LIBWEBSERV_PRIVATE void AddProtocolHandler(
+      std::unique_ptr<ProtocolHandler> handler);
+
+  // Private implementation of D-Bus RequestHandlerInterface called by the web
+  // server daemon whenever a new request is available to be processed.
+  std::unique_ptr<RequestHandler> request_handler_;
+  // D-Bus object adaptor for RequestHandlerInterface.
+  std::unique_ptr<org::chromium::WebServer::RequestHandlerAdaptor>
+      dbus_adaptor_;
+  // D-Bus object to handler registration of RequestHandlerInterface.
+  std::unique_ptr<chromeos::dbus_utils::DBusObject> dbus_object_;
+
+  // A mapping of protocol handler IDs to the associated object.
+  // Handler IDs are either GUIDs or the two well-known handler IDs.
+  std::map<std::string, std::unique_ptr<ProtocolHandler>> protocol_handlers_;
+  // A map between D-Bus object path of protocol handler and remote protocol
+  // handler ID.
+  std::map<dbus::ObjectPath, std::string> protocol_handler_id_map_;
+
+  // User-specified callbacks for server and protocol handler life-time events.
+  base::Closure on_server_online_;
+  base::Closure on_server_offline_;
+  base::Callback<void(ProtocolHandler*)> on_protocol_handler_connected_;
+  base::Callback<void(ProtocolHandler*)> on_protocol_handler_disconnected_;
+
+  // D-Bus object manager proxy that receives notification of web server
+  // daemon's D-Bus object creation and destruction.
+  std::unique_ptr<org::chromium::WebServer::ObjectManagerProxy> object_manager_;
+
+  // D-Bus proxy for the web server main object.
+  org::chromium::WebServer::ServerProxy* proxy_{nullptr};
+
+  // D-Bus service name used by the daemon hosting this object.
+  std::string service_name_;
+
   DISALLOW_COPY_AND_ASSIGN(Server);
 };
 
diff --git a/webservd/dbus_bindings/org.chromium.WebServer.Manager.xml b/webservd/dbus_bindings/org.chromium.WebServer.Manager.xml
deleted file mode 100644
index 8cd829f..0000000
--- a/webservd/dbus_bindings/org.chromium.WebServer.Manager.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-
-<node name="/org/chromium/WebServer/Manager"
-      xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
-  <interface name="org.chromium.WebServer.Manager">
-    <method name="Ping">
-      <arg name="message" type="s" direction="out"/>
-      <annotation name="org.chromium.DBus.Method.Kind" value="simple"/>
-    </method>
-  </interface>
-</node>
diff --git a/webservd/dbus_bindings/org.chromium.WebServer.ProtocolHandler.xml b/webservd/dbus_bindings/org.chromium.WebServer.ProtocolHandler.xml
new file mode 100644
index 0000000..438514a
--- /dev/null
+++ b/webservd/dbus_bindings/org.chromium.WebServer.ProtocolHandler.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+  <interface name="org.chromium.WebServer.ProtocolHandler">
+    <!-- Methods -->
+    <method name="AddRequestHandler">
+      <tp:docstring>
+        Adds a handler for the given |url|, and optionally request |method|.
+        On success returns a handler ID.
+      </tp:docstring>
+      <arg name="url" type="s" direction="in"/>
+      <arg name="method" type="s" direction="in"/>
+      <arg name="service_name" type="s" direction="in"/>
+      <arg name="request_handler_id" type="s" direction="out"/>
+      <annotation name="org.chromium.DBus.Method.Kind" value="simple"/>
+    </method>
+    <method name="RemoveRequestHandler">
+      <tp:docstring>
+        Removes a previously registered request handler.
+        The |handler_id| is the ID returned from AddHanlder() method.
+      </tp:docstring>
+      <arg name="request_handler_id" type="s" direction="in"/>
+      <annotation name="org.chromium.DBus.Method.Kind" value="normal"/>
+    </method>
+    <method name="GetRequestFileData">
+      <tp:docstring>
+        Returns the contents of the given uploaded file. The |file_id| parameter
+        must correspond to the file_id member of FileInfo structure returned
+        by |Files| property for the given |request_id|.
+      </tp:docstring>
+      <arg name="request_id" type="s" direction="in"/>
+      <arg name="file_id" type="i" direction="in"/>
+      <arg name="contents" type="ay" direction="out"/>
+      <annotation name="org.chromium.DBus.Method.Kind" value="normal"/>
+    </method>
+    <method name="CompleteRequest">
+      <tp:docstring>
+        Fulfills the request with specified |request_id| and provides response.
+      </tp:docstring>
+      <arg name="request_id" type="s" direction="in"/>
+      <arg name="status_code" type="i" direction="in"/>
+      <arg name="headers" type="a(ss)" direction="in"/>
+      <arg name="data" type="ay" direction="in"/>
+      <annotation name="org.chromium.DBus.Method.Kind" value="normal"/>
+    </method>
+    <!-- Properties -->
+    <property name="Id" type="s" access="read">
+      <tp:docstring>
+        Returns a unique ID of this instance.
+      </tp:docstring>
+    </property>
+    <property name="Port" type="q" access="read">
+      <tp:docstring>
+        Returns the port number this instance is serving requests on.
+      </tp:docstring>
+    </property>
+    <property name="Protocol" type="s" access="read">
+      <tp:docstring>
+        Returns the protocol name of this instance ("http" or "https").
+      </tp:docstring>
+    </property>
+    <property name="CertificateFingerprint" type="ay" access="read">
+      <tp:docstring>
+        Returns the TLS certificate fingerprint used for HTTPS instance or
+        empty array if this is an unsecured HTTP instance.
+      </tp:docstring>
+    </property>
+  </interface>
+</node>
diff --git a/webservd/dbus_bindings/org.chromium.WebServer.Server.xml b/webservd/dbus_bindings/org.chromium.WebServer.Server.xml
index 88d78c9..bff6e94 100644
--- a/webservd/dbus_bindings/org.chromium.WebServer.Server.xml
+++ b/webservd/dbus_bindings/org.chromium.WebServer.Server.xml
@@ -1,6 +1,23 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 
-<node xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+<node name="/org/chromium/WebServer/Server"
+      xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
   <interface name="org.chromium.WebServer.Server">
+    <!-- Methods -->
+    <method name="Ping">
+      <arg name="message" type="s" direction="out"/>
+      <annotation name="org.chromium.DBus.Method.Kind" value="simple"/>
+    </method>
+    <!-- Properties -->
+    <property name="DefaultHttp" type="o" access="read">
+      <tp:docstring>
+        Returns the D-Bus object path for the default HTTP protocol handler.
+      </tp:docstring>
+    </property>
+    <property name="DefaultHttps" type="o" access="read">
+      <tp:docstring>
+        Returns the D-Bus object path for the default HTTPS protocol handler.
+      </tp:docstring>
+    </property>
   </interface>
 </node>
diff --git a/webservd/dbus_protocol_handler.cc b/webservd/dbus_protocol_handler.cc
new file mode 100644
index 0000000..63a83b3
--- /dev/null
+++ b/webservd/dbus_protocol_handler.cc
@@ -0,0 +1,180 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webserver/webservd/dbus_protocol_handler.h"
+
+#include <base/bind.h>
+#include <chromeos/dbus/async_event_sequencer.h>
+#include <chromeos/dbus/exported_object_manager.h>
+
+#include "webserver/webservd/dbus_request_handler.h"
+#include "webserver/webservd/protocol_handler.h"
+#include "webserver/webservd/request.h"
+#include "webserver/webservd/server.h"
+
+using chromeos::dbus_utils::AsyncEventSequencer;
+using chromeos::dbus_utils::DBusObject;
+using chromeos::dbus_utils::ExportedObjectManager;
+
+namespace webservd {
+
+DBusProtocolHandler::DBusProtocolHandler(
+    ExportedObjectManager* object_manager,
+    const dbus::ObjectPath& object_path,
+    ProtocolHandler* protocol_handler,
+    Server* server)
+    : dbus_object_{new DBusObject{object_manager, object_manager->GetBus(),
+                                  object_path}},
+      protocol_handler_{protocol_handler},
+      server_{server} {
+  dbus_adaptor_.SetId(protocol_handler->GetID());
+  dbus_adaptor_.SetPort(protocol_handler->GetPort());
+  dbus_adaptor_.SetProtocol(protocol_handler->GetProtocol());
+  dbus_adaptor_.SetCertificateFingerprint(
+      protocol_handler->GetCertificateFingerprint());
+}
+
+void DBusProtocolHandler::RegisterAsync(
+    const AsyncEventSequencer::CompletionAction& completion_callback) {
+  scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
+  dbus_adaptor_.RegisterWithDBusObject(dbus_object_.get());
+  dbus_object_->RegisterAsync(
+      sequencer->GetHandler("Failed exporting ProtocolHandler.", true));
+  sequencer->OnAllTasksCompletedCall({completion_callback});
+}
+
+ExportedObjectManager* DBusProtocolHandler::GetObjectManager() const {
+  return dbus_object_->GetObjectManager().get();
+}
+
+std::string DBusProtocolHandler::AddRequestHandler(
+    const std::string& in_url,
+    const std::string& in_method,
+    const std::string& in_service_name) {
+  auto p = dbus_service_data_.find(in_service_name);
+  if (p == dbus_service_data_.end()) {
+    DBusServiceData dbus_service_data;
+    dbus_service_data.handler_proxy.reset(
+        new RequestHandlerProxy{server_->GetBus(), in_service_name});
+    dbus_service_data.on_client_disconnected_callback =
+        base::Bind(&DBusProtocolHandler::OnClientDisconnected,
+                   weak_ptr_factory_.GetWeakPtr(),
+                   in_service_name);
+    server_->GetBus()->ListenForServiceOwnerChange(
+        in_service_name, dbus_service_data.on_client_disconnected_callback);
+    p = dbus_service_data_.emplace(in_service_name,
+                                   std::move(dbus_service_data)).first;
+  }
+  std::unique_ptr<RequestHandlerInterface> handler{
+    new DBusRequestHandler{server_, p->second.handler_proxy.get()}
+  };
+  std::string handler_id = protocol_handler_->AddRequestHandler(
+      in_url, in_method, std::move(handler));
+  p->second.handler_ids.insert(handler_id);
+  handler_to_service_name_map_.emplace(handler_id, in_service_name);
+
+  return handler_id;
+}
+
+bool DBusProtocolHandler::RemoveRequestHandler(
+    chromeos::ErrorPtr* error,
+    const std::string& in_handler_id) {
+
+  auto p = handler_to_service_name_map_.find(in_handler_id);
+  if (p == handler_to_service_name_map_.end()) {
+    chromeos::Error::AddToPrintf(error,
+                                 FROM_HERE,
+                                 chromeos::errors::dbus::kDomain,
+                                 DBUS_ERROR_FAILED,
+                                 "Handler with ID %s does not exist",
+                                 in_handler_id.c_str());
+    return false;
+  }
+  std::string service_name = p->second;
+  CHECK(protocol_handler_->RemoveRequestHandler(in_handler_id));
+  DBusServiceData& dbus_service_data = dbus_service_data_[service_name];
+  CHECK_EQ(1u, dbus_service_data.handler_ids.erase(in_handler_id));
+  if (dbus_service_data.handler_ids.empty()) {
+    server_->GetBus()->UnlistenForServiceOwnerChange(
+        service_name, dbus_service_data.on_client_disconnected_callback);
+    dbus_service_data_.erase(service_name);
+  }
+  return true;
+}
+
+void DBusProtocolHandler::OnClientDisconnected(
+    const std::string& service_name,
+    const std::string& service_owner) {
+  // This method will be called when the client's D-Bus service owner has
+  // changed which could be either the client exiting (|service_owner| is empty)
+  // or is being replaced with another running instance.
+  // In either case, we need to remove the old client's handlers since the
+  // new client will register their own on start up anyway.
+  auto p = dbus_service_data_.find(service_name);
+  if (p == dbus_service_data_.end())
+    return;
+
+  for (const std::string& handler_id : p->second.handler_ids) {
+    handler_to_service_name_map_.erase(handler_id);
+    protocol_handler_->RemoveRequestHandler(handler_id);
+  }
+  server_->GetBus()->UnlistenForServiceOwnerChange(
+      service_name, p->second.on_client_disconnected_callback);
+  dbus_service_data_.erase(p);
+}
+
+bool DBusProtocolHandler::GetRequestFileData(
+    chromeos::ErrorPtr* error,
+    const std::string& in_request_id,
+    int32_t in_file_id,
+    std::vector<uint8_t>* out_contents) {
+  auto request = GetRequest(in_request_id, error);
+  if (!request)
+    return false;
+
+  if (request->GetFileData(in_file_id, out_contents))
+    return true;
+
+  chromeos::Error::AddToPrintf(error,
+                               FROM_HERE,
+                               chromeos::errors::dbus::kDomain,
+                               DBUS_ERROR_FAILED,
+                               "File with ID %d does not exist",
+                               in_file_id);
+  return false;
+}
+
+bool DBusProtocolHandler::CompleteRequest(
+    chromeos::ErrorPtr* error,
+    const std::string& in_request_id,
+    int32_t in_status_code,
+    const std::vector<std::tuple<std::string, std::string>>& in_headers,
+    const std::vector<uint8_t>& in_data) {
+  auto request = GetRequest(in_request_id, error);
+  if (!request)
+    return false;
+
+  if (request->Complete(in_status_code, in_headers, in_data)) {
+    return true;
+  }
+  chromeos::Error::AddTo(error, FROM_HERE, chromeos::errors::dbus::kDomain,
+                         DBUS_ERROR_FAILED, "Response already received");
+  return false;
+}
+
+Request* DBusProtocolHandler::GetRequest(const std::string& request_id,
+                                         chromeos::ErrorPtr* error) {
+  Request* request = protocol_handler_->GetRequest(request_id);
+  if (!request) {
+    chromeos::Error::AddToPrintf(error,
+                                 FROM_HERE,
+                                 chromeos::errors::dbus::kDomain,
+                                 DBUS_ERROR_FAILED,
+                                 "Unknown request ID: %s",
+                                 request_id.c_str());
+  }
+  return request;
+}
+
+}  // namespace webservd
diff --git a/webservd/dbus_protocol_handler.h b/webservd/dbus_protocol_handler.h
new file mode 100644
index 0000000..d616f49
--- /dev/null
+++ b/webservd/dbus_protocol_handler.h
@@ -0,0 +1,121 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBSERVER_WEBSERVD_DBUS_PROTOCOL_HANDLER_H_
+#define WEBSERVER_WEBSERVD_DBUS_PROTOCOL_HANDLER_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include <base/macros.h>
+#include <base/memory/weak_ptr.h>
+#include <chromeos/dbus/dbus_object.h>
+#include <dbus/bus.h>
+
+#include "libwebserv/dbus-proxies.h"
+#include "webservd/org.chromium.WebServer.ProtocolHandler.h"
+
+namespace chromeos {
+namespace dbus_utils {
+class ExportedObjectManager;
+}  // dbus_utils
+}  // chromeos
+
+namespace webservd {
+
+class ProtocolHandler;
+class Request;
+class Server;
+
+// This is a D-Bus interface object for the internal ProtocolHandler class.
+class DBusProtocolHandler final
+    : public org::chromium::WebServer::ProtocolHandlerInterface {
+ public:
+  DBusProtocolHandler(
+      chromeos::dbus_utils::ExportedObjectManager* object_manager,
+      const dbus::ObjectPath& object_path,
+      ProtocolHandler* protocol_handler,
+      Server* server);
+  void RegisterAsync(
+      const chromeos::dbus_utils::AsyncEventSequencer::CompletionAction& cb);
+
+  // Returns the instance of D-Bus exported object manager.
+  chromeos::dbus_utils::ExportedObjectManager* GetObjectManager() const;
+
+  // Overrides from org::chromium::WebServer::DBusProtocolHandlerInterface.
+  std::string AddRequestHandler(
+      const std::string& in_url,
+      const std::string& in_method,
+      const std::string& in_service_name) override;
+
+  bool RemoveRequestHandler(chromeos::ErrorPtr* error,
+                            const std::string& in_request_handler_id) override;
+
+  bool GetRequestFileData(chromeos::ErrorPtr* error,
+                          const std::string& in_request_id,
+                          int32_t in_file_id,
+                          std::vector<uint8_t>* out_contents) override;
+
+  bool CompleteRequest(
+      chromeos::ErrorPtr* error,
+      const std::string& in_request_id,
+      int32_t in_status_code,
+      const std::vector<std::tuple<std::string, std::string>>& in_headers,
+      const std::vector<uint8_t>& in_data) override;
+
+ private:
+  using RequestHandlerProxy = org::chromium::WebServer::RequestHandlerProxy;
+
+  // Information about a request handler D-Bus back-end client.
+  struct DBusServiceData {
+    // A D-Bus proxy to the client's request handler object that actually
+    // processes requests registered for this client.
+    std::unique_ptr<RequestHandlerProxy> handler_proxy;
+    // A list of handler IDs registered by this client.
+    std::set<std::string> handler_ids;
+    // Called when the owner of the well known service name associated with this
+    // client changes.  Since clients start up, this is called for the first
+    // time when they die.
+    dbus::Bus::GetServiceOwnerCallback on_client_disconnected_callback;
+  };
+
+  // Looks up a request with |request_id|.
+  // Returns nullptr and sets additional |error| information, if not found.
+  Request* GetRequest(const std::string& request_id, chromeos::ErrorPtr* error);
+
+  // Callback invoked when a client owning |service_name| is changed.
+  void OnClientDisconnected(const std::string& service_name,
+                            const std::string& service_owner);
+
+  // D-Bus object adaptor for ProtocolHandler D-Bus object.
+  org::chromium::WebServer::ProtocolHandlerAdaptor dbus_adaptor_{this};
+  std::unique_ptr<chromeos::dbus_utils::DBusObject> dbus_object_;
+
+  // Reference back to the real ProtocolHandler object.
+  ProtocolHandler* protocol_handler_{nullptr};
+  // Reference back to the Server class.
+  Server* server_{nullptr};
+
+  // Called when the owner of a service name changes.  We're only interested in
+  // transitions to the empty string, indicating that a service name owner has
+  // died.
+  dbus::Bus::GetServiceOwnerCallback on_client_disconnected_callback_;
+
+  // A map that holds information regarding a server back-end client processing
+  // requests on the D-Bus service with the name used in the key of the map.
+  std::map<std::string, DBusServiceData> dbus_service_data_;
+  // Handler ID to service name map.
+  std::map<std::string, std::string> handler_to_service_name_map_;
+
+  base::WeakPtrFactory<DBusProtocolHandler> weak_ptr_factory_{this};
+  DISALLOW_COPY_AND_ASSIGN(DBusProtocolHandler);
+};
+
+}  // namespace webservd
+
+#endif  // WEBSERVER_WEBSERVD_DBUS_PROTOCOL_HANDLER_H_
diff --git a/webservd/dbus_request_handler.cc b/webservd/dbus_request_handler.cc
new file mode 100644
index 0000000..8e44dd2
--- /dev/null
+++ b/webservd/dbus_request_handler.cc
@@ -0,0 +1,80 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webserver/webservd/dbus_request_handler.h"
+
+#include <tuple>
+#include <vector>
+
+#include <base/bind.h>
+#include <chromeos/http/http_request.h>
+#include <chromeos/mime_utils.h>
+
+#include "libwebserv/dbus-proxies.h"
+#include "webserver/webservd/request.h"
+#include "webserver/webservd/server.h"
+
+namespace webservd {
+
+namespace {
+
+void OnError(Request* request,
+             bool debug,
+             chromeos::Error* error) {
+  std::string error_msg{"Internal Server Error"};
+  if (debug) {
+    error_msg += "\r\n" + error->GetMessage();
+  }
+  request->Complete(chromeos::http::status_code::InternalServerError, {},
+                    chromeos::mime::text::kPlain, error_msg);
+}
+
+}  // anonymous namespace
+
+DBusRequestHandler::DBusRequestHandler(Server* server,
+                                       RequestHandlerProxy* handler_proxy)
+    : server_{server},
+      handler_proxy_{handler_proxy} {
+}
+
+void DBusRequestHandler::HandleRequest(Request* request) {
+  std::vector<std::tuple<std::string, std::string>> headers;
+  for (const auto& pair : request->GetHeaders())
+    headers.emplace_back(pair.first, pair.second);
+
+  std::vector<std::tuple<int32_t, std::string, std::string, std::string,
+                         std::string>> files;
+  int32_t index = 0;
+  for (const auto& file : request->GetFileInfo()) {
+    files.emplace_back(index++, file->field_name, file->file_name,
+                        file->content_type, file->transfer_encoding);
+  }
+
+  std::vector<std::tuple<bool, std::string, std::string>> params;
+  for (const auto& pair : request->GetDataGet())
+    params.emplace_back(false, pair.first, pair.second);
+
+  for (const auto& pair : request->GetDataPost())
+    params.emplace_back(true, pair.first, pair.second);
+
+  auto error_callback = base::Bind(&OnError,
+                                   base::Unretained(request),
+                                   server_->UseDebugInfo());
+
+  auto request_id = std::make_tuple(request->GetProtocolHandlerID(),
+                                    request->GetRequestHandlerID(),
+                                    request->GetID(),
+                                    request->GetURL(),
+                                    request->GetMethod());
+
+  handler_proxy_->ProcessRequestAsync(request_id,
+                                      headers,
+                                      params,
+                                      files,
+                                      request->GetBody(),
+                                      base::Bind(&base::DoNothing),
+                                      error_callback);
+}
+
+}  // namespace webservd
diff --git a/webservd/dbus_request_handler.h b/webservd/dbus_request_handler.h
new file mode 100644
index 0000000..f7a0617
--- /dev/null
+++ b/webservd/dbus_request_handler.h
@@ -0,0 +1,40 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBSERVER_WEBSERVD_DBUS_REQUEST_HANDLER_H_
+#define WEBSERVER_WEBSERVD_DBUS_REQUEST_HANDLER_H_
+
+#include <string>
+
+#include <base/macros.h>
+#include <dbus/object_path.h>
+
+#include "libwebserv/dbus-proxies.h"
+#include "webserver/webservd/request_handler_interface.h"
+
+namespace webservd {
+
+class Server;
+
+// A D-Bus interface for a request handler.
+class DBusRequestHandler : public RequestHandlerInterface {
+ public:
+  using RequestHandlerProxy = org::chromium::WebServer::RequestHandlerProxy;
+  DBusRequestHandler(Server* server,
+                     RequestHandlerProxy* handler_proxy);
+
+  // Called to process an incoming HTTP request this handler is subscribed
+  // to handle.
+  void HandleRequest(Request* request) override;
+
+ private:
+  Server* server_{nullptr};
+  RequestHandlerProxy* handler_proxy_{nullptr};
+
+  DISALLOW_COPY_AND_ASSIGN(DBusRequestHandler);
+};
+
+}  // namespace webservd
+
+#endif  // WEBSERVER_WEBSERVD_DBUS_REQUEST_HANDLER_H_
diff --git a/webservd/etc/init/webservd.conf b/webservd/etc/init/webservd.conf
index 08740d9..08cb7f2 100644
--- a/webservd/etc/init/webservd.conf
+++ b/webservd/etc/init/webservd.conf
@@ -5,19 +5,63 @@
 description     "Brillo WebServer Daemon"
 author          "chromium-os-dev@chromium.org"
 
-start on starting system-services
+start on starting system-services and stopped iptables and stopped ip6tables
 stop on stopping system-services
 respawn
 
+env WEBSERVD_LOG_LEVEL=0
+env WEBSERVD_HTTP_PORT=80
+env WEBSERVD_HTTPS_PORT=443
+env WEBSERVD_DEBUG=false
+# TODO(zqiu): Temporary
+env APMANAGER_DHCP_SERVER_PORT=67
+
 pre-start script
+  # Add a rule to the firewall to allow HTTP traffic except from the
+  # loopback interface (to prevent e.g. Chrome from connecting.)
+  # TODO(avakulenko): localhost is left enabled for now for testing...
+  #iptables -I INPUT -i lo -p tcp --dport ${WEBSERVD_HTTP_PORT} -j REJECT
+  #ip6tables -I INPUT -i lo -p tcp --dport ${WEBSERVD_HTTP_PORT} -j REJECT
+  #iptables -I INPUT -i lo -p tcp --dport ${WEBSERVD_HTTPS_PORT} -j REJECT
+  #ip6tables -I INPUT -i lo -p tcp --dport ${WEBSERVD_HTTPS_PORT} -j REJECT
+  iptables -A INPUT -p tcp --dport ${WEBSERVD_HTTP_PORT} -j ACCEPT
+  ip6tables -A INPUT -p tcp --dport ${WEBSERVD_HTTP_PORT} -j ACCEPT
+  iptables -A INPUT -p tcp --dport ${WEBSERVD_HTTPS_PORT} -j ACCEPT
+  ip6tables -A INPUT -p tcp --dport ${WEBSERVD_HTTPS_PORT} -j ACCEPT
+
+  # TODO(zqiu): Temporary hack for adding firewall rule to accept DHCP traffic
+  # on behalf of apmanager. Putting it here instead of in the apmanager upstart
+  # script to avoid a race condition when multiple upstart scripts tried to
+  # execute iptables command at the same time. Will delete this when the
+  # support for runtime firewall hole punching in permission_broker is
+  # completed (crbug.com/435400).
+  iptables -I INPUT -p udp --dport ${APMANAGER_DHCP_SERVER_PORT} -j ACCEPT
+  iptables -I INPUT -i lo -p udp --dport ${APMANAGER_DHCP_SERVER_PORT} -j REJECT
+
   mkdir -m 0755 -p /var/log/webservd
   chown webservd:webservd /var/log/webservd
 end script
 
-env WEBSERVD_LOG_LEVEL=0
+exec /usr/bin/webservd \
+    --v="${WEBSERVD_LOG_LEVEL}" \
+    --http_port="${WEBSERVD_HTTP_PORT}" \
+    --https_port="${WEBSERVD_HTTPS_PORT}" \
+    --debug="${WEBSERVD_DEBUG}" \
 
-# Minijail actually forks off our desired process.
-expect fork
+post-stop script
+  # Delete the rules we previously added
+  iptables -D INPUT -p tcp --dport ${WEBSERVD_HTTP_PORT} -j ACCEPT
+  ip6tables -D INPUT -p tcp --dport ${WEBSERVD_HTTP_PORT} -j ACCEPT
+  iptables -D INPUT -p tcp --dport ${WEBSERVD_HTTPS_PORT} -j ACCEPT
+  ip6tables -D INPUT -p tcp --dport ${WEBSERVD_HTTPS_PORT} -j ACCEPT
 
-exec minijail0 -i -g webservd -u webservd /usr/bin/webservd \
-    --v="${WEBSERVD_LOG_LEVEL}"
+  # TODO(zqiu): refer to comments above for adding these rules.
+  iptables -D INPUT -p udp --dport ${APMANAGER_DHCP_SERVER_PORT} -j ACCEPT
+  iptables -D INPUT -i lo -p udp --dport ${APMANAGER_DHCP_SERVER_PORT} -j REJECT
+
+  # TODO(avakulenko): uncomment below
+  #iptables -D INPUT -i lo -p tcp --dport ${WEBSERVD_HTTP_PORT} -j REJECT
+  #ip6tables -D INPUT -i lo -p tcp --dport ${WEBSERVD_HTTP_PORT} -j REJECT
+  #iptables -D INPUT -i lo -p tcp --dport ${WEBSERVD_HTTPS_PORT} -j REJECT
+  #ip6tables -D INPUT -i lo -p tcp --dport ${WEBSERVD_HTTPS_PORT} -j REJECT
+end script
diff --git a/webservd/main.cc b/webservd/main.cc
index 9c9e385..97e05eb 100644
--- a/webservd/main.cc
+++ b/webservd/main.cc
@@ -3,14 +3,17 @@
 // found in the LICENSE file.
 
 #include <string>
+#include <sysexits.h>
 
 #include <base/command_line.h>
 #include <chromeos/dbus/async_event_sequencer.h>
 #include <chromeos/dbus/exported_object_manager.h>
 #include <chromeos/daemons/dbus_daemon.h>
+#include <chromeos/flag_helper.h>
+#include <chromeos/minijail/minijail.h>
 #include <chromeos/syslog_logging.h>
 
-#include "webserver/webservd/manager.h"
+#include "webserver/webservd/server.h"
 
 using chromeos::dbus_utils::AsyncEventSequencer;
 
@@ -18,21 +21,32 @@
 
 const char kServiceName[] = "org.chromium.WebServer";
 const char kRootServicePath[] = "/org/chromium/WebServer";
+const char kWebServerUserName[] = "webservd";
+const char kWebServerGroupName[] = "webservd";
 
 class Daemon : public chromeos::DBusServiceDaemon {
  public:
-  Daemon() : DBusServiceDaemon(kServiceName, kRootServicePath) {}
+  Daemon(uint16_t http_port, uint16_t https_port, bool debug)
+      : DBusServiceDaemon{kServiceName, kRootServicePath},
+        http_port_{http_port},
+        https_port_{https_port},
+        debug_{debug} {}
 
  protected:
   void RegisterDBusObjectsAsync(AsyncEventSequencer* sequencer) override {
-    manager_.reset(new webservd::Manager{object_manager_.get()});
-    manager_->RegisterAsync(
-        sequencer->GetHandler("Manager.RegisterAsync() failed.", true));
-    LOG(INFO) << "webservd starting...";
+    server_.reset(new webservd::Server{object_manager_.get(),
+                                       http_port_,
+                                       https_port_,
+                                       debug_});
+    server_->RegisterAsync(
+        sequencer->GetHandler("Server.RegisterAsync() failed.", true));
   }
 
  private:
-  std::unique_ptr<webservd::Manager> manager_;
+  uint16_t http_port_{0};
+  uint16_t https_port_{0};
+  bool debug_{false};
+  std::unique_ptr<webservd::Server> server_;
 
   DISALLOW_COPY_AND_ASSIGN(Daemon);
 };
@@ -40,10 +54,44 @@
 }  // namespace
 
 int main(int argc, char* argv[]) {
-  CommandLine::Init(argc, argv);
-  chromeos::InitLog(chromeos::kLogToSyslog |
-                    chromeos::kLogToStderr |
-                    chromeos::kLogHeader);
-  Daemon daemon;
+  DEFINE_int32(http_port, 80,
+               "HTTP port to listen for requests on (0 to disable)");
+  DEFINE_int32(https_port, 443,
+               "HTTPS port to listen for requests on (0 to disable)");
+  DEFINE_bool(log_to_stderr, false, "log trace messages to stderr as well");
+  DEFINE_bool(debug, false,
+              "return debug error information in web requests");
+  chromeos::FlagHelper::Init(argc, argv, "Brillo web server daemon");
+
+  int flags = chromeos::kLogToSyslog;
+  if (FLAGS_log_to_stderr)
+    flags |= chromeos::kLogToStderr;
+  chromeos::InitLog(flags | chromeos::kLogHeader);
+
+  if (FLAGS_http_port < 0 || FLAGS_http_port > 0xFFFF) {
+    LOG(ERROR) << "Invalid HTTP port specified: '" << FLAGS_http_port << "'.";
+    return EX_USAGE;
+  }
+
+  if (FLAGS_https_port < 0 || FLAGS_https_port > 0xFFFF) {
+    LOG(ERROR) << "Invalid HTTPS port specified: '" << FLAGS_https_port << "'.";
+    return EX_USAGE;
+  }
+
+  Daemon daemon{static_cast<uint16_t>(FLAGS_http_port),
+                static_cast<uint16_t>(FLAGS_https_port),
+                FLAGS_debug};
+
+  // Drop privileges and use 'webservd' user. We need to do this after Daemon
+  // object is constructed since it creates an instance of base::AtExitManager
+  // which is required for chromeos::Minijail::GetInstance() to work.
+  chromeos::Minijail* minijail_instance = chromeos::Minijail::GetInstance();
+  minijail* jail = minijail_instance->New();
+  minijail_instance->DropRoot(jail, kWebServerUserName, kWebServerGroupName);
+  // Permissions needed for the daemon to allow it to bind to ports like TCP
+  // 80.
+  minijail_instance->UseCapabilities(jail, CAP_TO_MASK(CAP_NET_BIND_SERVICE));
+  minijail_enter(jail);
+  minijail_instance->Destroy(jail);
   return daemon.Run();
 }
diff --git a/webservd/manager.cc b/webservd/manager.cc
deleted file mode 100644
index c228a09..0000000
--- a/webservd/manager.cc
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "webserver/webservd/manager.h"
-
-#include <chromeos/dbus/async_event_sequencer.h>
-#include <chromeos/dbus/exported_object_manager.h>
-
-using chromeos::dbus_utils::AsyncEventSequencer;
-using chromeos::dbus_utils::DBusObject;
-using chromeos::dbus_utils::ExportedObjectManager;
-
-namespace webservd {
-
-Manager::Manager(ExportedObjectManager* object_manager)
-  : dbus_object_{new DBusObject{
-        object_manager, object_manager->GetBus(),
-        org::chromium::WebServer::ManagerAdaptor::GetObjectPath()}} {}
-
-void Manager::RegisterAsync(
-    const AsyncEventSequencer::CompletionAction& completion_callback) {
-  scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
-  dbus_adaptor_.RegisterWithDBusObject(dbus_object_.get());
-  dbus_object_->RegisterAsync(
-      sequencer->GetHandler("Failed exporting Manager.", true));
-  sequencer->OnAllTasksCompletedCall({completion_callback});
-}
-
-std::string Manager::Ping() {
-  return "Web Server is running";
-}
-
-}  // namespace webservd
diff --git a/webservd/manager.h b/webservd/manager.h
deleted file mode 100644
index 89ae864..0000000
--- a/webservd/manager.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef WEBSERVER_WEBSERVD_MANAGER_H_
-#define WEBSERVER_WEBSERVD_MANAGER_H_
-
-#include <memory>
-#include <string>
-
-#include <base/macros.h>
-#include <chromeos/dbus/dbus_object.h>
-
-#include "webservd/org.chromium.WebServer.Manager.h"
-
-namespace chromeos {
-namespace dbus_utils {
-class ExportedObjectManager;
-}  // dbus_utils
-}  // chromeos
-
-namespace webservd {
-
-// Manages global state of webservd.
-class Manager : public org::chromium::WebServer::ManagerInterface {
- public:
-  explicit Manager(chromeos::dbus_utils::ExportedObjectManager* object_manager);
-  void RegisterAsync(
-      const chromeos::dbus_utils::AsyncEventSequencer::CompletionAction& cb);
-
-  // Overrides from org::chromium::WebServer::ManagerInterface.
-  std::string Ping() override;
-
- private:
-  org::chromium::WebServer::ManagerAdaptor dbus_adaptor_{this};
-  std::unique_ptr<chromeos::dbus_utils::DBusObject> dbus_object_;
-  DISALLOW_COPY_AND_ASSIGN(Manager);
-};
-
-}  // namespace webservd
-
-#endif  // WEBSERVER_WEBSERVD_MANAGER_H_
diff --git a/webservd/protocol_handler.cc b/webservd/protocol_handler.cc
new file mode 100644
index 0000000..402d865
--- /dev/null
+++ b/webservd/protocol_handler.cc
@@ -0,0 +1,326 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webserver/webservd/protocol_handler.h"
+
+#include <limits>
+#include <microhttpd.h>
+
+#include <base/bind.h>
+#include <base/guid.h>
+#include <base/logging.h>
+#include <base/message_loop/message_loop.h>
+
+#include "webserver/webservd/request.h"
+#include "webserver/webservd/request_handler_interface.h"
+#include "webserver/webservd/server_interface.h"
+
+namespace webservd {
+
+const char ProtocolHandler::kHttp[] = "http";
+const char ProtocolHandler::kHttps[] = "https";
+
+// Helper class to provide static callback methods to libmicrohttpd library,
+// with the ability to access private methods of Server class.
+class ServerHelper {
+ public:
+  static int ConnectionHandler(void *cls,
+                               MHD_Connection* connection,
+                               const char* url,
+                               const char* method,
+                               const char* version,
+                               const char* upload_data,
+                               size_t* upload_data_size,
+                               void** con_cls) {
+    auto handler = reinterpret_cast<ProtocolHandler*>(cls);
+    if (nullptr == *con_cls) {
+      std::string request_handler_id = handler->FindRequestHandler(url, method);
+      std::unique_ptr<Request> request{new Request{
+          request_handler_id, url, method, connection, handler
+      }};
+      if (!request->BeginRequestData())
+        return MHD_NO;
+
+      // Pass the raw pointer here in order to interface with libmicrohttpd's
+      // old-style C API.
+      *con_cls = request.release();
+    } else {
+      auto request = reinterpret_cast<Request*>(*con_cls);
+      if (*upload_data_size) {
+        if (!request->AddRequestData(upload_data, *upload_data_size))
+          return MHD_NO;
+        *upload_data_size = 0;
+      } else {
+        request->EndRequestData();
+      }
+    }
+    return MHD_YES;
+  }
+
+  static void RequestCompleted(void* cls,
+                               MHD_Connection* connection,
+                               void** con_cls,
+                               MHD_RequestTerminationCode toe) {
+    auto request = reinterpret_cast<Request*>(*con_cls);
+    *con_cls = nullptr;
+    delete request;
+  }
+};
+
+ProtocolHandler::ProtocolHandler(const std::string& id,
+                                 ServerInterface* server_interface)
+    : id_{id}, server_interface_{server_interface} {
+  if (id_.empty())
+    id_ = base::GenerateGUID();
+}
+
+ProtocolHandler::~ProtocolHandler() {
+  Stop();
+}
+
+std::string ProtocolHandler::AddRequestHandler(
+    const std::string& url,
+    const std::string& method,
+    std::unique_ptr<RequestHandlerInterface> handler) {
+  std::string handler_id = base::GenerateGUID();
+  request_handlers_.emplace(handler_id,
+                            HandlerMapEntry{url, method, std::move(handler)});
+  return handler_id;
+}
+
+bool ProtocolHandler::RemoveRequestHandler(const std::string& handler_id) {
+  return request_handlers_.erase(handler_id) == 1;
+}
+
+std::string ProtocolHandler::FindRequestHandler(
+    const base::StringPiece& url,
+    const base::StringPiece& method) const {
+  size_t score = std::numeric_limits<size_t>::max();
+  std::string handler_id;
+  for (const auto& pair : request_handlers_) {
+    std::string handler_url = pair.second.url;
+    bool url_match = (handler_url == url);
+    bool method_match = (pair.second.method == method);
+
+    // Try exact match first. If everything matches, we have our handler.
+    if (url_match && method_match)
+      return pair.first;
+
+    // Calculate the current handler's similarity score. The lower the score
+    // the better the match is...
+    size_t current_score = 0;
+    if (!url_match && !handler_url.empty() && handler_url.back() == '/') {
+      if (url.starts_with(handler_url)) {
+        url_match = true;
+        // Use the difference in URL length as URL match quality proxy.
+        // The longer URL, the more specific (better) match is.
+        // Multiply by 2 to allow for extra score point for matching the method.
+        current_score = (url.size() - handler_url.size()) * 2;
+      }
+    }
+
+    if (!method_match && pair.second.method.empty()) {
+      // If the handler didn't specify the method it handles, this means
+      // it doesn't care. However this isn't the exact match, so bump
+      // the score up one point.
+      method_match = true;
+      ++current_score;
+    }
+
+    if (url_match && method_match && current_score < score) {
+      score = current_score;
+      handler_id = pair.first;
+    }
+  }
+
+  return handler_id;
+}
+
+bool ProtocolHandler::Start(uint16_t port) {
+  return StartWithTLS(port, {}, {}, {});
+}
+
+bool ProtocolHandler::StartWithTLS(
+    uint16_t port,
+    const chromeos::SecureBlob& private_key,
+    const chromeos::Blob& certificate,
+    const chromeos::Blob& certificate_fingerprint) {
+  if (server_) {
+    LOG(ERROR) << "Web server is already running.";
+    return false;
+  }
+
+  // Either both keys and certificate must be specified or both must be omitted.
+  CHECK_EQ(private_key.empty(), certificate.empty());
+  // Same with cert fingerprint.
+  CHECK_EQ(certificate.empty(), certificate_fingerprint.empty());
+
+  const bool use_tls = !private_key.empty();
+
+  LOG(INFO) << "Starting " << (use_tls ? "HTTPS" : "HTTP")
+            << " Server on port: " << port;
+
+  port_ = port;
+  protocol_ = (use_tls ? "https" : "http");
+  certificate_fingerprint_ = certificate_fingerprint;
+
+  auto callback_addr =
+      reinterpret_cast<intptr_t>(&ServerHelper::RequestCompleted);
+  uint32_t flags = MHD_NO_FLAG;
+  if (server_interface_->UseDebugInfo())
+    flags |= MHD_USE_DEBUG;
+
+  std::vector<MHD_OptionItem> options{
+    {MHD_OPTION_CONNECTION_LIMIT, 10, nullptr},
+    {MHD_OPTION_CONNECTION_TIMEOUT, 60, nullptr},
+    {MHD_OPTION_NOTIFY_COMPLETED, callback_addr, nullptr},
+  };
+
+  // libmicrohttpd expects both the key and certificate to be zero-terminated
+  // strings. Make sure they are terminated properly.
+  chromeos::SecureBlob private_key_copy = private_key;
+  chromeos::Blob certificate_copy = certificate;
+  private_key_copy.push_back(0);
+  certificate_copy.push_back(0);
+
+  if (use_tls) {
+    flags |= MHD_USE_SSL;
+    options.push_back(
+        MHD_OptionItem{MHD_OPTION_HTTPS_MEM_KEY, 0, private_key_copy.data()});
+    options.push_back(
+        MHD_OptionItem{MHD_OPTION_HTTPS_MEM_CERT, 0, certificate_copy.data()});
+  }
+
+  options.push_back(MHD_OptionItem{MHD_OPTION_END, 0, nullptr});
+
+  server_ = MHD_start_daemon(flags, port, nullptr, nullptr,
+                             &ServerHelper::ConnectionHandler, this,
+                             MHD_OPTION_ARRAY, options.data(), MHD_OPTION_END);
+  if (!server_) {
+    LOG(ERROR) << "Failed to start the web server on port " << port;
+    return false;
+  }
+  server_interface_->ProtocolHandlerStarted(this);
+  DoWork();
+  LOG(INFO) << "Server started";
+  return true;
+}
+
+bool ProtocolHandler::Stop() {
+  if (server_) {
+    LOG(INFO) << "Shutting down the web server...";
+    MHD_stop_daemon(server_);
+    server_ = nullptr;
+    server_interface_->ProtocolHandlerStopped(this);
+    LOG(INFO) << "Server shutdown complete";
+  }
+  port_ = 0;
+  protocol_.clear();
+  certificate_fingerprint_.clear();
+  return true;
+}
+
+void ProtocolHandler::AddRequest(Request* request) {
+  requests_.emplace(request->GetID(), request);
+}
+
+void ProtocolHandler::RemoveRequest(Request* request) {
+  requests_.erase(request->GetID());
+}
+
+Request* ProtocolHandler::GetRequest(const std::string& request_id) const {
+  auto p = requests_.find(request_id);
+  return (p != requests_.end()) ? p->second : nullptr;
+}
+
+// A file descriptor watcher class that oversees I/O operation notification
+// on particular socket file descriptor.
+class ProtocolHandler::Watcher final
+    : public base::MessageLoopForIO::Watcher{
+ public:
+  Watcher(ProtocolHandler* handler,
+          int fd,
+          base::MessageLoopForIO::Mode mode,
+          base::MessageLoopForIO* message_loop)
+      : handler_{handler} {
+    message_loop->WatchFileDescriptor(fd, false, mode, &controller_, this);
+  }
+
+  // Overrides from base::MessageLoopForIO::Watcher.
+  void OnFileCanReadWithoutBlocking(int fd) override {
+    handler_->DoWork();
+  }
+
+  void OnFileCanWriteWithoutBlocking(int fd) override {
+    handler_->DoWork();
+  }
+
+ private:
+  ProtocolHandler* handler_;
+  base::MessageLoopForIO::FileDescriptorWatcher controller_;
+
+  DISALLOW_COPY_AND_ASSIGN(Watcher);
+};
+
+void ProtocolHandler::OnResponseDataReceived() {
+  base::MessageLoopForIO::current()->PostTask(
+      FROM_HERE,
+      base::Bind(&ProtocolHandler::DoWork, weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ProtocolHandler::DoWork() {
+  base::MessageLoopForIO* message_loop = base::MessageLoopForIO::current();
+
+  // Remove the old watchers first.
+  watchers_.clear();
+
+  // Check if there is any pending work to be done in libmicrohttpd.
+  MHD_run(server_);
+
+  // Get all the file descriptors from libmicrohttpd and watch for I/O
+  // operations on them.
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  int max_fd = MHD_INVALID_SOCKET;
+  FD_ZERO(&rs);
+  FD_ZERO(&ws);
+  FD_ZERO(&es);
+  CHECK_EQ(MHD_YES, MHD_get_fdset(server_, &rs, &ws, &es, &max_fd));
+  for (int fd = 0; fd <= max_fd; fd++) {
+    // libmicrohttpd is not using exception FDs, so lets put our expectations
+    // upfront.
+    CHECK(!FD_ISSET(fd, &es));
+    if (FD_ISSET(fd, &rs) || FD_ISSET(fd, &ws)) {
+      base::MessageLoopForIO::Mode mode = base::MessageLoopForIO::WATCH_READ;
+      if (FD_ISSET(fd, &rs) && FD_ISSET(fd, &ws))
+        mode = base::MessageLoopForIO::WATCH_READ_WRITE;
+      else if (FD_ISSET(fd, &rs))
+        mode = base::MessageLoopForIO::WATCH_READ;
+      else if (FD_ISSET(fd, &ws))
+        mode = base::MessageLoopForIO::WATCH_WRITE;
+      // libmicrohttpd should never use any of stdin/stdout/stderr descriptors.
+      CHECK_GT(fd, STDERR_FILENO);
+      watchers_.emplace_back(new Watcher{this, fd, mode, message_loop});
+    }
+  }
+
+  // Schedule a time-out timer, if asked by libmicrohttpd.
+  MHD_UNSIGNED_LONG_LONG mhd_timeout = 0;
+  if (!timer_scheduled_ && MHD_get_timeout(server_, &mhd_timeout) == MHD_YES) {
+    timer_scheduled_ = true;
+    message_loop->PostDelayedTask(
+        FROM_HERE,
+        base::Bind(&ProtocolHandler::DoWork,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::TimeDelta::FromMilliseconds(mhd_timeout));
+  }
+}
+
+void ProtocolHandler::TimerCallback() {
+  timer_scheduled_ = false;
+  DoWork();
+}
+
+}  // namespace webservd
diff --git a/webservd/protocol_handler.h b/webservd/protocol_handler.h
new file mode 100644
index 0000000..f99dcf3
--- /dev/null
+++ b/webservd/protocol_handler.h
@@ -0,0 +1,136 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBSERVER_WEBSERVD_PROTOCOL_HANDLER_H_
+#define WEBSERVER_WEBSERVD_PROTOCOL_HANDLER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+#include <base/memory/weak_ptr.h>
+#include <base/strings/string_piece.h>
+#include <chromeos/secure_blob.h>
+
+struct MHD_Daemon;
+
+namespace webservd {
+
+class Request;
+class RequestHandlerInterface;
+class ServerInterface;
+
+// An instance of a handler for particular protocol (http/https) bound to a
+// particular port to handle requests on.
+class ProtocolHandler final {
+ public:
+  ProtocolHandler(const std::string& id,
+                  ServerInterface* server_interface);
+  ~ProtocolHandler();
+
+  // Registers a new request handler for the given URL and request method.
+  // Returns a handler ID (GUID).
+  std::string AddRequestHandler(
+      const std::string& url,
+      const std::string& method,
+      std::unique_ptr<RequestHandlerInterface> handler);
+
+  // Removes a previously registered handler.
+  bool RemoveRequestHandler(const std::string& handler_id);
+
+  // Finds a handler for given URL/Method. This is the method used to look up
+  // the handler for incoming HTTP requests.
+  // Returns the handler_id or empty string if not found.
+  std::string FindRequestHandler(const base::StringPiece& url,
+                                 const base::StringPiece& method) const;
+  // Binds the socket and listens to HTTP requests on it.
+  bool Start(uint16_t port);
+
+  // Binds the socket and listens to HTTPS requests on it.
+  bool StartWithTLS(uint16_t port,
+                    const chromeos::SecureBlob& private_key,
+                    const chromeos::Blob& certificate,
+                    const chromeos::Blob& certificate_fingerprint);
+
+  // Stops listening for requests.
+  bool Stop();
+
+  // Returns the port this handler listens for requests on.
+  uint16_t GetPort() const { return port_; }
+
+  // Returns the protocol name for this handler ("http" or "https").
+  const std::string& GetProtocol() const { return protocol_; }
+
+  // Returns the SHA-256 fingerprint of the TLS certificate used for https
+  // connection. Returns an empty byte array if this handler is serving http.
+  const chromeos::Blob& GetCertificateFingerprint() const {
+    return certificate_fingerprint_;
+  }
+
+  // Returns the unique request handler ID. This will normally by a GUID, unless
+  // this is one of the two default handlers for HTTP/HTTPS protocols with
+  // default ports, in which case the IDs will be "http" and "https"
+  // respectively.
+  const std::string& GetID() const { return id_; }
+
+  // Returns the pointer to the Server object.
+  ServerInterface* GetServer() const { return server_interface_; }
+
+  // Methods to store/remove/retrieve pending incoming requests for the duration
+  // of the request's processing.
+  void AddRequest(Request* request);
+  void RemoveRequest(Request* request);
+  Request* GetRequest(const std::string& request_id) const;
+
+  // Notification of incoming reply from the request handler.
+  void OnResponseDataReceived();
+
+  static const char kHttp[];
+  static const char kHttps[];
+
+ private:
+  friend class Request;
+  friend class ServerHelper;
+  class Watcher;
+  struct HandlerMapEntry {
+    std::string url;
+    std::string method;
+    std::unique_ptr<RequestHandlerInterface> handler;
+  };
+
+  // Called when new data is available on sockets for libmicrohttpd to process.
+  void DoWork();
+  // Called periodically as requested by libmicrohttpd.
+  void TimerCallback();
+
+  // libmicrohttpd daemon class.
+  MHD_Daemon* server_{nullptr};
+  // A map that stores registered request handlers (the key is handler ID).
+  std::map<std::string, HandlerMapEntry> request_handlers_;
+  // A map that stores pending requests (the key is request ID).
+  std::map<std::string, Request*> requests_;
+  // Protocol Handler ID.
+  std::string id_;
+  // Reference back to the Server.
+  ServerInterface* server_interface_{nullptr};
+  // The port we are listening to.
+  uint16_t port_{0};
+  // The protocol name ("http" or "https").
+  std::string protocol_;
+  // TLS certificate fingerprint (if any).
+  chromeos::Blob certificate_fingerprint_;
+  // File descriptor watchers for current active sockets.
+  std::vector<std::unique_ptr<Watcher>> watchers_;
+  // Set to true when a timer request is scheduled.
+  bool timer_scheduled_{false};
+
+  base::WeakPtrFactory<ProtocolHandler> weak_ptr_factory_{this};
+  DISALLOW_COPY_AND_ASSIGN(ProtocolHandler);
+};
+
+}  // namespace webservd
+
+#endif  // WEBSERVER_WEBSERVD_PROTOCOL_HANDLER_H_
diff --git a/webservd/request.cc b/webservd/request.cc
new file mode 100644
index 0000000..11a5d38
--- /dev/null
+++ b/webservd/request.cc
@@ -0,0 +1,240 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webserver/webservd/request.h"
+
+#include <microhttpd.h>
+
+#include <base/bind.h>
+#include <base/guid.h>
+#include <chromeos/http/http_request.h>
+#include <chromeos/http/http_utils.h>
+#include <chromeos/mime_utils.h>
+#include <chromeos/strings/string_utils.h>
+#include "webserver/webservd/protocol_handler.h"
+#include "webserver/webservd/request_handler_interface.h"
+
+namespace webservd {
+
+// Helper class to provide static callback methods to microhttpd library,
+// with the ability to access private methods of Request class.
+class RequestHelper {
+ public:
+  static int PostDataIterator(void* cls,
+                              MHD_ValueKind kind,
+                              const char* key,
+                              const char* filename,
+                              const char* content_type,
+                              const char* transfer_encoding,
+                              const char* data,
+                              uint64_t off,
+                              size_t size) {
+    auto self = reinterpret_cast<Request*>(cls);
+    return self->ProcessPostData(key, filename, content_type, transfer_encoding,
+                                 data, off, size) ? MHD_YES : MHD_NO;
+  }
+
+  static int ValueCallback(void* cls,
+                           MHD_ValueKind kind,
+                           const char* key,
+                           const char* value) {
+    auto self = reinterpret_cast<Request*>(cls);
+    std::string data;
+    if (value)
+      data = value;
+    if (kind == MHD_HEADER_KIND) {
+      self->headers_.emplace_back(chromeos::http::GetCanonicalHeaderName(key),
+                                  data);
+    } else if (kind == MHD_COOKIE_KIND) {
+      // TODO(avakulenko): add support for cookies...
+    } else if (kind == MHD_POSTDATA_KIND) {
+      self->post_data_.emplace_back(key, data);
+    } else if (kind == MHD_GET_ARGUMENT_KIND) {
+      self->get_data_.emplace_back(key, data);
+    }
+    return MHD_YES;
+  }
+};
+
+FileInfo::FileInfo(const std::string& in_field_name,
+                   const std::string& in_file_name,
+                   const std::string& in_content_type,
+                   const std::string& in_transfer_encoding)
+    : field_name(in_field_name),
+      file_name(in_file_name),
+      content_type(in_content_type),
+      transfer_encoding(in_transfer_encoding) {
+}
+
+Request::Request(
+    const std::string& request_handler_id,
+    const std::string& url,
+    const std::string& method,
+    MHD_Connection* connection,
+    ProtocolHandler* protocol_handler)
+    : id_{base::GenerateGUID()},
+      request_handler_id_{request_handler_id},
+      url_{url},
+      method_{method},
+      connection_{connection},
+      protocol_handler_{protocol_handler} {
+  post_processor_ = MHD_create_post_processor(
+      connection, 1024, &RequestHelper::PostDataIterator, this);
+}
+
+Request::~Request() {
+  if (post_processor_)
+    MHD_destroy_post_processor(post_processor_);
+  protocol_handler_->RemoveRequest(this);
+}
+
+bool Request::GetFileData(int file_id, std::vector<uint8_t>* contents) {
+  if (file_id < 0 || static_cast<size_t>(file_id) >= file_info_.size())
+    return false;
+  *contents = file_info_[file_id]->data;
+  return true;
+}
+
+bool Request::Complete(
+    int32_t status_code,
+    const std::vector<std::tuple<std::string, std::string>>& headers,
+    const std::vector<uint8_t>& data) {
+  if (state_ != State::kWaitingForResponse)
+    return false;
+
+  response_status_code_ = status_code;
+  response_headers_.reserve(headers.size());
+  for (const auto& tuple : headers) {
+    response_headers_.emplace_back(std::get<0>(tuple), std::get<1>(tuple));
+  }
+  response_data_ = data;
+  state_ = State::kResponseReceived;
+  protocol_handler_->OnResponseDataReceived();
+  return true;
+}
+
+bool Request::Complete(
+    int32_t status_code,
+    const std::vector<std::tuple<std::string, std::string>>& headers,
+    const std::string& mime_type,
+    const std::string& data) {
+  std::vector<std::tuple<std::string, std::string>> headers_copy;
+  headers_copy.emplace_back(chromeos::http::response_header::kContentType,
+                            mime_type);
+  return Complete(status_code, headers_copy,
+                  chromeos::string_utils::GetStringAsBytes(data));
+}
+
+const std::string& Request::GetProtocolHandlerID() const {
+  return protocol_handler_->GetID();
+}
+
+bool Request::BeginRequestData() {
+  MHD_get_connection_values(connection_, MHD_HEADER_KIND,
+                            &RequestHelper::ValueCallback, this);
+  MHD_get_connection_values(connection_, MHD_COOKIE_KIND,
+                            &RequestHelper::ValueCallback, this);
+  MHD_get_connection_values(connection_, MHD_POSTDATA_KIND,
+                            &RequestHelper::ValueCallback, this);
+  MHD_get_connection_values(connection_, MHD_GET_ARGUMENT_KIND,
+                            &RequestHelper::ValueCallback, this);
+  return true;
+}
+
+bool Request::AddRequestData(const void* data, size_t size) {
+  if (!post_processor_)
+    return AddRawRequestData(data, size);
+  return MHD_post_process(post_processor_,
+                          static_cast<const char*>(data), size) == MHD_YES;
+}
+
+void Request::EndRequestData() {
+  if (state_ == State::kIdle) {
+    state_ = State::kWaitingForResponse;
+    if (!request_handler_id_.empty()) {
+      protocol_handler_->AddRequest(this);
+      auto p = protocol_handler_->request_handlers_.find(request_handler_id_);
+      CHECK(p != protocol_handler_->request_handlers_.end());
+      // Send the request over D-Bus and await the response...
+      p->second.handler->HandleRequest(this);
+    } else {
+      // There was no handler found when request was made, respond with
+      // 404 Page Not Found...
+      Complete(chromeos::http::status_code::NotFound, {},
+               chromeos::mime::text::kPlain, "Not Found");
+    }
+  } else if (state_ == State::kResponseReceived) {
+    MHD_Response* resp = MHD_create_response_from_buffer(
+        response_data_.size(), response_data_.data(), MHD_RESPMEM_PERSISTENT);
+    for (const auto& pair : response_headers_) {
+      MHD_add_response_header(resp, pair.first.c_str(), pair.second.c_str());
+    }
+    CHECK_EQ(MHD_YES,
+             MHD_queue_response(connection_, response_status_code_, resp))
+        << "Failed to queue response";
+    MHD_destroy_response(resp);  // |resp| is ref-counted.
+    state_ = State::kDone;
+  }
+}
+
+bool Request::ProcessPostData(const char* key,
+                              const char* filename,
+                              const char* content_type,
+                              const char* transfer_encoding,
+                              const char* data,
+                              uint64_t off,
+                              size_t size) {
+  if (off > 0)
+    return AppendPostFieldData(key, data, size);
+
+  return  AddPostFieldData(key, filename, content_type, transfer_encoding, data,
+                           size);
+}
+
+
+bool Request::AddRawRequestData(const void* data, size_t size) {
+  const uint8_t* byte_data_ = static_cast<const uint8_t*>(data);
+  raw_data_.insert(raw_data_.end(), byte_data_, byte_data_ + size);
+  return true;
+}
+
+bool Request::AddPostFieldData(const char* key,
+                               const char* filename,
+                               const char* content_type,
+                               const char* transfer_encoding,
+                               const char* data,
+                               size_t size) {
+  if (filename) {
+    std::unique_ptr<FileInfo> file_info{
+        new FileInfo{key, filename, content_type ? content_type : "",
+                     transfer_encoding ? transfer_encoding : ""}};
+    file_info->data.assign(data, data + size);
+    file_info_.push_back(std::move(file_info));
+    last_posted_data_was_file_ = true;
+    return true;
+  }
+  std::string value{data, size};
+  post_data_.emplace_back(key, value);
+  last_posted_data_was_file_ = false;
+  return true;
+}
+
+bool Request::AppendPostFieldData(const char* key,
+                                  const char* data,
+                                  size_t size) {
+  if (last_posted_data_was_file_) {
+    CHECK(!file_info_.empty());
+    CHECK(file_info_.back()->field_name == key);
+    FileInfo* file_info = file_info_.back().get();
+    file_info->data.insert(file_info->data.end(), data, data + size);
+    return true;
+  }
+
+  CHECK(!post_data_.empty());
+  CHECK(post_data_.back().first == key);
+  post_data_.back().second.append(data, size);
+  return true;
+}
+
+}  // namespace webservd
diff --git a/webservd/request.h b/webservd/request.h
new file mode 100644
index 0000000..15b3184
--- /dev/null
+++ b/webservd/request.h
@@ -0,0 +1,170 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBSERVER_WEBSERVD_REQUEST_H_
+#define WEBSERVER_WEBSERVD_REQUEST_H_
+
+#include <memory>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include <base/macros.h>
+
+struct MHD_Connection;
+struct MHD_PostProcessor;
+
+namespace webservd {
+
+class ProtocolHandler;
+
+using PairOfStrings = std::pair<std::string, std::string>;
+
+// This class represents the file information about a file uploaded via
+// POST request using multipart/form-data request.
+class FileInfo final {
+ public:
+  FileInfo(const std::string& in_field_name,
+           const std::string& in_file_name,
+           const std::string& in_content_type,
+           const std::string& in_transfer_encoding);
+
+  // The name of the form field for the file upload.
+  std::string field_name;
+  // The name of the file name specified in the form field.
+  std::string file_name;
+  // The content type of the file data.
+  std::string content_type;
+  // Data transfer encoding specified. Could be empty if no transfer encoding
+  // was specified.
+  std::string transfer_encoding;
+  // The file content data.
+  std::vector<uint8_t> data;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FileInfo);
+};
+
+// A class that represents the HTTP request data.
+class Request final {
+ public:
+  Request(const std::string& request_handler_id,
+          const std::string& url,
+          const std::string& method,
+          MHD_Connection* connection,
+          ProtocolHandler* protocol_handler);
+  ~Request();
+
+  // Obtains the content data of uploaded file identified by |file_id|.
+  bool GetFileData(int file_id, std::vector<uint8_t>* contents);
+
+  // Finishes the request and provides the reply data.
+  bool Complete(
+      int32_t status_code,
+      const std::vector<std::tuple<std::string, std::string>>& headers,
+      const std::vector<uint8_t>& data);
+
+  // Helper function to provide the string data and mime type.
+  bool Complete(
+      int32_t status_code,
+      const std::vector<std::tuple<std::string, std::string>>& headers,
+      const std::string& mime_type,
+      const std::string& data);
+
+  // Returns the unique ID of this request (GUID).
+  const std::string& GetID() const { return id_; }
+
+  // Returns the unique ID of the request handler this request is processed by
+  // (GUID).
+  const std::string& GetRequestHandlerID() const { return request_handler_id_; }
+
+  // Returns the unique ID of the protocol handler this request is received
+  // from (GUID or "http"/"https" for the two default handlers).
+  const std::string& GetProtocolHandlerID() const;
+
+  // Returns the object path of the HTTP request (e.g. "/privet/info").
+  const std::string& GetURL() const { return url_; }
+
+  // Returns the request method (e.g. "GET", "POST", ...).
+  const std::string& GetMethod() const { return method_; }
+
+  // Returns the raw body of the request, or empty byte array of the request
+  // had no body or a POST request has been parsed into form data.
+  const std::vector<uint8_t>& GetBody() const { return raw_data_; }
+
+  // Returns the POST form field data.
+  const std::vector<PairOfStrings>& GetDataPost() const { return post_data_; }
+
+  // Returns query parameters specified on the URL (as in "?param=value").
+  const std::vector<PairOfStrings>& GetDataGet() const { return get_data_; }
+
+  // Returns the information about any files uploaded as part of POST request.
+  const std::vector<std::unique_ptr<FileInfo>>& GetFileInfo() const {
+    return file_info_;
+  }
+
+  // Returns the HTTP request headers.
+  const std::vector<PairOfStrings>& GetHeaders() const { return headers_; }
+
+ private:
+  friend class RequestHelper;
+  friend class ServerHelper;
+
+  enum class State { kIdle, kWaitingForResponse, kResponseReceived, kDone };
+
+  // Helper methods for processing request data coming from the raw HTTP
+  // connection.
+  // Helper callback methods used by ProtocolHandler's ConnectionHandler to
+  // transfer request headers and data to the Request object.
+  bool BeginRequestData();
+  bool AddRequestData(const void* data, size_t size);
+  void EndRequestData();
+
+  // Callback for libmicrohttpd's PostProcessor.
+  bool ProcessPostData(const char* key,
+                       const char* filename,
+                       const char* content_type,
+                       const char* transfer_encoding,
+                       const char* data,
+                       uint64_t off,
+                       size_t size);
+
+  // These methods parse the request headers and data so they can be accessed
+  // by request handlers later.
+  bool AddRawRequestData(const void* data, size_t size);
+  bool AddPostFieldData(const char* key,
+                        const char* filename,
+                        const char* content_type,
+                        const char* transfer_encoding,
+                        const char* data,
+                        size_t size);
+  bool AppendPostFieldData(const char* key, const char* data, size_t size);
+
+  std::string id_;
+  std::string request_handler_id_;
+  std::string url_;
+  std::string method_;
+  MHD_Connection* connection_{nullptr};
+  MHD_PostProcessor* post_processor_{nullptr};
+  std::vector<uint8_t> raw_data_;
+  bool last_posted_data_was_file_{false};
+  State state_{State::kIdle};
+
+  std::vector<PairOfStrings> post_data_;
+  std::vector<PairOfStrings> get_data_;
+  std::vector<std::unique_ptr<FileInfo>> file_info_;
+  std::vector<PairOfStrings> headers_;
+
+  int response_status_code_{0};
+  std::vector<uint8_t> response_data_;
+  std::vector<PairOfStrings> response_headers_;
+  ProtocolHandler* protocol_handler_;
+
+  DISALLOW_COPY_AND_ASSIGN(Request);
+};
+
+}  // namespace webservd
+
+#endif  // WEBSERVER_WEBSERVD_REQUEST_H_
diff --git a/webservd/request_handler_interface.h b/webservd/request_handler_interface.h
new file mode 100644
index 0000000..1cde9c4
--- /dev/null
+++ b/webservd/request_handler_interface.h
@@ -0,0 +1,31 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBSERVER_WEBSERVD_REQUEST_HANDLER_INTERFACE_H_
+#define WEBSERVER_WEBSERVD_REQUEST_HANDLER_INTERFACE_H_
+
+#include <string>
+
+#include <base/macros.h>
+
+namespace webservd {
+
+class Request;
+
+// An abstract interface to dispatch of HTTP requests to remote handlers over
+// implementation-specific IPC (e.g. D-Bus).
+class RequestHandlerInterface {
+ public:
+  RequestHandlerInterface() = default;
+  virtual ~RequestHandlerInterface() = default;
+
+  virtual void HandleRequest(Request* request) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(RequestHandlerInterface);
+};
+
+}  // namespace webservd
+
+#endif  // WEBSERVER_WEBSERVD_REQUEST_HANDLER_INTERFACE_H_
diff --git a/webservd/server.cc b/webservd/server.cc
new file mode 100644
index 0000000..87f1654
--- /dev/null
+++ b/webservd/server.cc
@@ -0,0 +1,156 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webserver/webservd/server.h"
+
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+
+#include <limits>
+
+#include <base/rand_util.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/dbus/async_event_sequencer.h>
+#include <chromeos/dbus/exported_object_manager.h>
+
+#include "webserver/webservd/dbus_protocol_handler.h"
+#include "webserver/webservd/protocol_handler.h"
+#include "webserver/webservd/utils.h"
+
+using chromeos::dbus_utils::AsyncEventSequencer;
+using chromeos::dbus_utils::DBusObject;
+using chromeos::dbus_utils::ExportedObjectManager;
+
+namespace webservd {
+
+Server::Server(ExportedObjectManager* object_manager,
+               uint16_t http_port,
+               uint16_t https_port,
+               bool debug)
+    : dbus_object_{new DBusObject{
+          object_manager, object_manager->GetBus(),
+          org::chromium::WebServer::ServerAdaptor::GetObjectPath()}},
+      http_port_{http_port},
+      https_port_{https_port},
+      debug_{debug} {
+  dbus_adaptor_.SetDefaultHttp(dbus::ObjectPath{"/"});
+  dbus_adaptor_.SetDefaultHttps(dbus::ObjectPath{"/"});
+}
+
+Server::~Server() {}
+
+void Server::RegisterAsync(
+    const AsyncEventSequencer::CompletionAction& completion_callback) {
+  scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
+  dbus_adaptor_.RegisterWithDBusObject(dbus_object_.get());
+
+  if (http_port_)
+    CreateProtocolHandler(http_port_, ProtocolHandler::kHttp, false);
+  if (https_port_)
+    CreateProtocolHandler(https_port_, ProtocolHandler::kHttps, true);
+
+  dbus_object_->RegisterAsync(
+      sequencer->GetHandler("Failed exporting Server.", true));
+
+  for (const auto& pair : protocol_handler_map_) {
+    pair.second->RegisterAsync(
+        sequencer->GetHandler("Failed exporting ProtocolHandler.", false));
+  }
+  sequencer->OnAllTasksCompletedCall({completion_callback});
+}
+
+std::string Server::Ping() {
+  return "Web Server is running";
+}
+
+void Server::ProtocolHandlerStarted(ProtocolHandler* handler) {
+  CHECK(protocol_handler_map_.find(handler) == protocol_handler_map_.end())
+      << "Protocol handler already registered";
+  std::string dbus_id;
+  if (handler->GetID() == ProtocolHandler::kHttp ||
+      handler->GetID() == ProtocolHandler::kHttps)
+    dbus_id = handler->GetID();
+  else
+    dbus_id = std::to_string(++last_protocol_handler_index_);
+
+  std::string path = base::StringPrintf("/org/chromium/WebServer/Servers/%s",
+                                        dbus_id.c_str());
+  dbus::ObjectPath object_path{path};
+  std::unique_ptr<DBusProtocolHandler> dbus_protocol_handler{
+      new DBusProtocolHandler{dbus_object_->GetObjectManager().get(),
+                              object_path,
+                              handler,
+                              this}
+  };
+  protocol_handler_map_.emplace(handler, std::move(dbus_protocol_handler));
+  if (handler->GetID() == ProtocolHandler::kHttp)
+    dbus_adaptor_.SetDefaultHttps(object_path);
+  else if (handler->GetID() == ProtocolHandler::kHttps)
+    dbus_adaptor_.SetDefaultHttp(object_path);
+}
+
+void Server::ProtocolHandlerStopped(ProtocolHandler* handler) {
+  CHECK_EQ(1u, protocol_handler_map_.erase(handler))
+      << "Unknown protocol handler";
+}
+
+void Server::CreateProtocolHandler(uint16_t port,
+                                   const std::string& id,
+                                   bool use_tls) {
+  std::unique_ptr<ProtocolHandler> protocol_handler{
+      new ProtocolHandler{id, this}};
+
+  if (use_tls) {
+    InitTlsData();
+    if (protocol_handler->StartWithTLS(port,
+                                       TLS_private_key_,
+                                       TLS_certificate_,
+                                       TLS_certificate_fingerprint_))
+      protocol_handlers_.emplace(id, std::move(protocol_handler));
+  } else {
+    if (protocol_handler->Start(port))
+      protocol_handlers_.emplace(id, std::move(protocol_handler));
+  }
+}
+
+void Server::InitTlsData() {
+  if (!TLS_certificate_.empty())
+    return;  // Already initialized.
+
+  // TODO(avakulenko): verify these constants and provide sensible values
+  // for the long-term.
+  const int kKeyLengthBits = 1024;
+  const base::TimeDelta kCertExpiration = base::TimeDelta::FromDays(365);
+  const char kCommonName[] = "Brillo device";
+
+  // Create the X509 certificate.
+  int cert_serial_number = base::RandInt(0, std::numeric_limits<int>::max());
+  auto cert =
+      CreateCertificate(cert_serial_number, kCertExpiration, kCommonName);
+
+  // Create RSA key pair.
+  auto rsa_key_pair = GenerateRSAKeyPair(kKeyLengthBits);
+
+  // Store the private key to a temp buffer.
+  // Do not assign it to |TLS_private_key_| yet until the end when we are sure
+  // everything else has worked out.
+  chromeos::SecureBlob private_key = StoreRSAPrivateKey(rsa_key_pair.get());
+
+  // Create EVP key and set it to the certificate.
+  auto key = std::unique_ptr<EVP_PKEY, void (*)(EVP_PKEY*)>{EVP_PKEY_new(),
+                                                            EVP_PKEY_free};
+  CHECK(key.get());
+  // Transfer ownership of |rsa_key_pair| to |key|.
+  CHECK(EVP_PKEY_assign_RSA(key.get(), rsa_key_pair.release()));
+  CHECK(X509_set_pubkey(cert.get(), key.get()));
+
+  // Sign the certificate.
+  CHECK(X509_sign(cert.get(), key.get(), EVP_sha256()));
+
+  TLS_certificate_ = StoreCertificate(cert.get());
+  TLS_certificate_fingerprint_ = GetSha256Fingerprint(cert.get());
+  TLS_private_key_ = std::move(private_key);
+}
+
+}  // namespace webservd
diff --git a/webservd/server.h b/webservd/server.h
new file mode 100644
index 0000000..c2bd2b2
--- /dev/null
+++ b/webservd/server.h
@@ -0,0 +1,81 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBSERVER_WEBSERVD_SERVER_H_
+#define WEBSERVER_WEBSERVD_SERVER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+#include <chromeos/dbus/dbus_object.h>
+#include <chromeos/secure_blob.h>
+
+#include "webservd/org.chromium.WebServer.Server.h"
+#include "webserver/webservd/server_interface.h"
+
+namespace chromeos {
+namespace dbus_utils {
+class ExportedObjectManager;
+}  // dbus_utils
+}  // chromeos
+
+namespace webservd {
+
+class DBusProtocolHandler;
+class DBusServerRequest;
+
+// Top-level D-Bus object to interface with the server as a whole.
+class Server final : public org::chromium::WebServer::ServerInterface,
+                     public ServerInterface {
+ public:
+  Server(chromeos::dbus_utils::ExportedObjectManager* object_manager,
+         uint16_t http_port,
+         uint16_t https_port,
+         bool debug);
+  // Need to off-line the destructor to allow |protocol_handler_map_| to contain
+  // a forward-declared pointer to DBusProtocolHandler.
+  ~Server();
+
+  void RegisterAsync(
+      const chromeos::dbus_utils::AsyncEventSequencer::CompletionAction& cb);
+
+  // Overrides from org::chromium::WebServer::ServerInterface.
+  std::string Ping() override;
+
+  // Overrides from webservd::ServerInterface.
+  void ProtocolHandlerStarted(ProtocolHandler* handler) override;
+  void ProtocolHandlerStopped(ProtocolHandler* handler) override;
+  bool UseDebugInfo() const override { return debug_; }
+
+  scoped_refptr<dbus::Bus> GetBus() { return dbus_object_->GetBus(); }
+
+ private:
+  void CreateProtocolHandler(uint16_t port,
+                             const std::string& id,
+                             bool use_tls);
+  void InitTlsData();
+
+  org::chromium::WebServer::ServerAdaptor dbus_adaptor_{this};
+  std::unique_ptr<chromeos::dbus_utils::DBusObject> dbus_object_;
+  uint16_t http_port_{0};
+  uint16_t https_port_{0};
+  bool debug_{false};
+  int last_protocol_handler_index_{0};
+  chromeos::Blob TLS_certificate_;
+  chromeos::Blob TLS_certificate_fingerprint_;
+  chromeos::SecureBlob TLS_private_key_;
+
+  std::map<ProtocolHandler*,
+           std::unique_ptr<DBusProtocolHandler>> protocol_handler_map_;
+  std::map<std::string, std::unique_ptr<ProtocolHandler>> protocol_handlers_;
+
+  DISALLOW_COPY_AND_ASSIGN(Server);
+};
+
+}  // namespace webservd
+
+#endif  // WEBSERVER_WEBSERVD_SERVER_H_
diff --git a/webservd/server_interface.h b/webservd/server_interface.h
new file mode 100644
index 0000000..165aabb
--- /dev/null
+++ b/webservd/server_interface.h
@@ -0,0 +1,46 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBSERVER_WEBSERVD_SERVER_INTERFACE_H_
+#define WEBSERVER_WEBSERVD_SERVER_INTERFACE_H_
+
+#include <base/macros.h>
+
+namespace webservd {
+
+class ProtocolHandler;
+
+// An abstract interface to expose Server object to IPC transport layer such as
+// D-Bus.
+class ServerInterface {
+ public:
+  ServerInterface() = default;
+
+  // Called by ProtocolHandler to notify the server that a new protocol handler
+  // appears online or goes offline.
+  virtual void ProtocolHandlerStarted(ProtocolHandler* handler) = 0;
+  virtual void ProtocolHandlerStopped(ProtocolHandler* handler) = 0;
+
+  // Helper method to obtain the Debug-Request flag. This flag controls
+  // additional logging from libmicrohttpd as well as more debug info sent
+  // to browser in case of an error response.
+  virtual bool UseDebugInfo() const = 0;
+
+ protected:
+  // This interface should not be used to control the life-time of the class
+  // that derives from this interface. This is especially important when a mock
+  // server class is used. Since the life-time of the mock must be controlled
+  // by the test itself, we can't let some business logic suddenly delete
+  // the instance of this interface.
+  // So, just declare the destructor as protected, so nobody can just call
+  // delete on a pointer to ServerInterface.
+  ~ServerInterface() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ServerInterface);
+};
+
+}  // namespace webservd
+
+#endif  // WEBSERVER_WEBSERVD_SERVER_INTERFACE_H_
diff --git a/webservd/utils.cc b/webservd/utils.cc
new file mode 100644
index 0000000..6c7da29
--- /dev/null
+++ b/webservd/utils.cc
@@ -0,0 +1,91 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webserver/webservd/utils.h"
+
+#include <openssl/bio.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+
+namespace webservd {
+
+std::unique_ptr<X509, void(*)(X509*)> CreateCertificate(
+    int serial_number,
+    const base::TimeDelta& cert_expiration,
+    const std::string& common_name) {
+  auto cert = std::unique_ptr<X509, void(*)(X509*)>{X509_new(), X509_free};
+  CHECK(cert.get());
+  X509_set_version(cert.get(), 2);  // Using X.509 v3 certificate...
+
+  // Set certificate properties...
+  ASN1_INTEGER* sn = X509_get_serialNumber(cert.get());
+  ASN1_INTEGER_set(sn, serial_number);
+  X509_gmtime_adj(X509_get_notBefore(cert.get()), 0);
+  X509_gmtime_adj(X509_get_notAfter(cert.get()), cert_expiration.InSeconds());
+
+  // The issuer is the same as the subject, since this cert is self-signed.
+  X509_NAME* name = X509_get_subject_name(cert.get());
+  if (!common_name.empty()) {
+    X509_NAME_add_entry_by_txt(
+        name, "CN", MBSTRING_UTF8,
+        reinterpret_cast<const unsigned char*>(common_name.c_str()),
+        common_name.length(), -1, 0);
+  }
+  X509_set_issuer_name(cert.get(), name);
+  return cert;
+}
+
+std::unique_ptr<RSA, void(*)(RSA*)> GenerateRSAKeyPair(int key_length_bits) {
+  // Create RSA key pair.
+  auto rsa_key_pair = std::unique_ptr<RSA, void(*)(RSA*)>{RSA_new(), RSA_free};
+  CHECK(rsa_key_pair.get());
+  auto big_num = std::unique_ptr<BIGNUM, void(*)(BIGNUM*)>{BN_new(), BN_free};
+  CHECK(big_num.get());
+  CHECK(BN_set_word(big_num.get(), 65537));
+  CHECK(RSA_generate_key_ex(rsa_key_pair.get(), key_length_bits, big_num.get(),
+                            nullptr));
+  return rsa_key_pair;
+}
+
+chromeos::SecureBlob StoreRSAPrivateKey(RSA* rsa_key_pair) {
+  auto bio =
+      std::unique_ptr<BIO, void(*)(BIO*)>{BIO_new(BIO_s_mem()), BIO_vfree};
+  CHECK(bio);
+  CHECK(PEM_write_bio_RSAPrivateKey(bio.get(), rsa_key_pair, nullptr, nullptr,
+                                    0, nullptr, nullptr));
+  uint8_t* buffer = nullptr;
+  size_t size = BIO_get_mem_data(bio.get(), &buffer);
+  CHECK_GT(size, 0u);
+  CHECK(buffer);
+  chromeos::SecureBlob key_blob(buffer, size);
+  chromeos::SecureMemset(buffer, 0, size);
+  return key_blob;
+}
+
+chromeos::Blob StoreCertificate(X509* cert) {
+  auto bio =
+      std::unique_ptr<BIO, void(*)(BIO*)>{BIO_new(BIO_s_mem()), BIO_vfree};
+  CHECK(bio);
+  CHECK(PEM_write_bio_X509(bio.get(), cert));
+  uint8_t* buffer = nullptr;
+  size_t size = BIO_get_mem_data(bio.get(), &buffer);
+  CHECK_GT(size, 0u);
+  CHECK(buffer);
+  return chromeos::Blob(buffer, buffer + size);
+}
+
+// Same as openssl x509 -fingerprint -sha256.
+chromeos::Blob GetSha256Fingerprint(X509* cert) {
+  chromeos::Blob fingerprint(256 / 8);
+  uint32_t len = 0;
+  CHECK(X509_digest(cert, EVP_sha256(), fingerprint.data(), &len));
+  CHECK_EQ(len, fingerprint.size());
+  return fingerprint;
+}
+
+}  // namespace webservd
diff --git a/webservd/utils.h b/webservd/utils.h
new file mode 100644
index 0000000..b84545a
--- /dev/null
+++ b/webservd/utils.h
@@ -0,0 +1,39 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBSERVER_WEBSERVD_UTILS_H_
+#define WEBSERVER_WEBSERVD_UTILS_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+#include <openssl/ossl_typ.h>
+
+#include <base/time/time.h>
+#include <chromeos/secure_blob.h>
+
+namespace webservd {
+
+// Creates a new X509 certificate.
+std::unique_ptr<X509, void(*)(X509*)> CreateCertificate(
+    int serial_number,
+    const base::TimeDelta& cert_expiration,
+    const std::string& common_name);
+
+// Generates an RSA public-private key pair of the specified strength.
+std::unique_ptr<RSA, void(*)(RSA*)> GenerateRSAKeyPair(int key_length_bits);
+
+// Serializes a private key from the key pair into a PEM string and returns
+// it as a binary blob.
+chromeos::SecureBlob StoreRSAPrivateKey(RSA* rsa_key_pair);
+
+// Serializes an X509 certificate using PEM format.
+chromeos::Blob StoreCertificate(X509* cert);
+
+// Same as openssl x509 -fingerprint -sha256.
+chromeos::Blob GetSha256Fingerprint(X509* cert);
+
+}  // namespace webservd
+
+#endif  // WEBSERVER_WEBSERVD_UTILS_H_
diff --git a/webserver.gyp b/webserver.gyp
index 8376c2b..f101abc 100644
--- a/webserver.gyp
+++ b/webserver.gyp
@@ -13,20 +13,23 @@
       'target_name': 'libwebserv-<(libbase_ver)',
       'type': 'shared_library',
       'variables': {
-        'exported_deps': [
-          'libmicrohttpd',
-        ],
-        'deps': ['<@(exported_deps)'],
-        'dbus_adaptors_out_dir': 'include/webservd',
-        'dbus_service_config': 'webservd/dbus_bindings/dbus-service-config.json',
+        # Not using dbus_service_config here deliberately in order not to
+        # get tied to some constant service name, since it will be
+        # provided by the consumer of libwebserv library.
+        'dbus_service_config': '',
+        'dbus_adaptors_out_dir': 'include/libwebserv',
       },
-      'includes': ['../common-mk/deps.gypi'],
+      'includes': [
+        '../common-mk/deps.gypi',
+        '../common-mk/generate-dbus-adaptors.gypi'
+      ],
       'sources': [
-        'libwebserv/connection.cc',
-        'libwebserv/response.cc',
+        'libwebserv/protocol_handler.cc',
         'libwebserv/request.cc',
         'libwebserv/request_handler_callback.cc',
+        'libwebserv/response.cc',
         'libwebserv/server.cc',
+        'libwebserv/dbus_bindings/org.chromium.WebServer.RequestHandler.xml',
       ],
       'actions': [
         {
@@ -35,9 +38,10 @@
             'dbus_service_config': 'webservd/dbus_bindings/dbus-service-config.json',
             'mock_output_file': 'include/webservd/dbus-mocks.h',
             'proxy_output_file': 'include/webservd/dbus-proxies.h',
+            'dbus_adaptors_out_dir': '',
           },
           'sources': [
-            'webservd/dbus_bindings/org.chromium.WebServer.Manager.xml',
+            'webservd/dbus_bindings/org.chromium.WebServer.ProtocolHandler.xml',
             'webservd/dbus_bindings/org.chromium.WebServer.Server.xml',
           ],
           'includes': ['../common-mk/generate-dbus-proxies.gypi'],
@@ -50,18 +54,46 @@
       'variables': {
         'exported_deps': [
           'libmicrohttpd',
+          'openssl',
         ],
         'deps': ['<@(exported_deps)'],
         'dbus_adaptors_out_dir': 'include/webservd',
         'dbus_service_config': 'webservd/dbus_bindings/dbus-service-config.json',
       },
+      'link_settings': {
+        'libraries': [
+          '-lminijail',
+        ],
+      },
       'sources': [
-        'webservd/dbus_bindings/org.chromium.WebServer.Manager.xml',
+        'webservd/dbus_bindings/org.chromium.WebServer.ProtocolHandler.xml',
         'webservd/dbus_bindings/org.chromium.WebServer.Server.xml',
+        'webservd/dbus_protocol_handler.cc',
+        'webservd/dbus_request_handler.cc',
         'webservd/main.cc',
-        'webservd/manager.cc',
+        'webservd/protocol_handler.cc',
+        'webservd/request.cc',
+        'webservd/server.cc',
+        'webservd/utils.cc',
       ],
       'includes': ['../common-mk/generate-dbus-adaptors.gypi'],
+      'actions': [
+        {
+          'action_name': 'generate-libwebserv-proxies',
+          'variables': {
+            # Not using dbus_service_config here deliberately in order not to
+            # get tied to some constant service name, since it will be
+            # provided by the consumer of libwebserv library.
+            'dbus_service_config': '',
+            'mock_output_file': 'include/libwebserv/dbus-mocks.h',
+            'proxy_output_file': 'include/libwebserv/dbus-proxies.h',
+          },
+          'sources': [
+            'libwebserv/dbus_bindings/org.chromium.WebServer.RequestHandler.xml',
+          ],
+          'includes': ['../common-mk/generate-dbus-proxies.gypi'],
+        },
+      ],
     },
   ],
   'conditions': [